Jelajahi Sumber

Add session validation tool

Zed 3 minggu lalu
induk
melakukan
206622ea38
2 mengubah file dengan 137 tambahan dan 1 penghapusan
  1. 1 1
      src/apiutils.nim
  2. 136 0
      tools/test_sessions.nim

+ 1 - 1
src/apiutils.nim

@@ -34,7 +34,7 @@ proc setApiProxy*(url: string) =
     if "http" notin apiProxy:
       apiProxy = "http://" & apiProxy
 
-proc toUrl(req: ApiReq; sessionKind: SessionKind): Uri =
+proc toUrl*(req: ApiReq; sessionKind: SessionKind): Uri =
   let url = case sessionKind
     of oauth:  req.oauth
     of cookie: req.cookie

+ 136 - 0
tools/test_sessions.nim

@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: AGPL-3.0-only
+# Test each cookie session in a JSONL file by hitting the UserMedia endpoint.
+#
+# Usage:
+#   nim r --path:src tools/test_sessions.nim [sessions_file] [--delay N]
+#
+# Examples:
+#   nim r --path:src tools/test_sessions.nim
+#   nim r --path:src tools/test_sessions.nim sessions_cookie.jsonl --delay 1
+
+import asyncdispatch, httpclient, strutils, uri, os, zippy
+import apiutils, auth, consts, types
+import experimental/parser/session
+
+const
+  # jack (id=12) — public account with media, same target as ratelimit_probe.py
+  testVars = userMediaVars % ["12", "", "20"]
+
+# Build the ApiReq once — same for every session
+let
+  testUrl = ApiUrl(endpoint: graphUserMedia,
+                   params: @[("variables", testVars), ("features", gqlFeatures)])
+  testReq = ApiReq(cookie: testUrl, oauth: testUrl)
+  url = testReq.toUrl(SessionKind.cookie)
+
+proc parseCookieSessions(path: string): seq[Session] =
+  var skipped = 0
+  for line in path.lines:
+    let s = line.strip()
+    if s.len == 0: continue
+    try:
+      let sess = parseSession(s)
+      if sess.kind == SessionKind.cookie:
+        result.add sess
+      else:
+        inc skipped
+    except Exception as e:
+      echo "  [!] Parse error: ", e.msg
+  if skipped > 0:
+    echo "  (skipped ", skipped, " non-cookie sessions)"
+
+proc testSession(session: Session): Future[tuple[ok: bool, code: int,
+    remaining, limit: int, detail: string]] {.async.} =
+  let headers = await genHeaders(session, url, skipTid = false)
+  let client = newAsyncHttpClient(headers = headers)
+  defer: client.close()
+
+  try:
+    let resp = await client.get($url)
+    var body = await resp.body
+    if resp.headers.getOrDefault("content-encoding") == "gzip":
+      body = uncompress(body, dfGzip)
+
+    var remaining, limit: int
+    if resp.headers.hasKey("x-rate-limit-remaining"):
+      remaining = parseInt(resp.headers["x-rate-limit-remaining"])
+    if resp.headers.hasKey("x-rate-limit-limit"):
+      limit = parseInt(resp.headers["x-rate-limit-limit"])
+
+    if resp.code == Http200:
+      return (true, resp.code.int, remaining, limit, "")
+    else:
+      let detail = if body.len in 1..120: body else: ""
+      return (false, resp.code.int, remaining, limit, detail)
+  except Exception as e:
+    return (false, 0, 0, 0, e.msg[0 ..< min(e.msg.len, 120)])
+
+proc main() {.async.} =
+  var
+    sessionsPath = "sessions.jsonl"
+    delay = 500  # ms
+    i = 1
+
+  while i <= paramCount():
+    let arg = paramStr(i)
+    case arg
+    of "--delay":
+      inc i
+      if i > paramCount():
+        echo "Error: --delay requires a value (seconds)"; quit(1)
+      delay = int(parseFloat(paramStr(i)) * 1000)
+    of "--help", "-h":
+      echo "Usage: nim r --path:src tools/test_sessions.nim [sessions_file] [--delay N]"
+      return
+    else:
+      sessionsPath = arg
+    inc i
+
+  if not fileExists(sessionsPath):
+    echo "File not found: ", sessionsPath; quit(1)
+
+  setApiProxy("")
+  setDisableTid(false)
+
+  let sessions = parseCookieSessions(sessionsPath)
+  if sessions.len == 0:
+    echo "No cookie sessions found in ", sessionsPath; quit(0)
+
+  echo "Testing ", sessions.len, " sessions from ", sessionsPath
+  echo ""
+
+  var
+    nValid, nFail: int
+    failures: seq[string]
+
+  for idx, session in sessions:
+    let
+      num = idx + 1
+      prefix = "[" & $num & "/" & $sessions.len & "] " & session.pretty
+      res = await testSession(session)
+
+    if res.ok:
+      inc nValid
+      var extra = ""
+      if res.limit > 0:
+        extra = "  remaining=" & $res.remaining & "/" & $res.limit
+      echo prefix, "  ✓ ", res.code, extra
+    else:
+      inc nFail
+      let detail = if res.code > 0: $res.code else: "error: " & res.detail
+      failures.add session.pretty & "  " & detail
+      echo prefix, "  ✗ ", detail
+
+    if num < sessions.len and delay > 0:
+      await sleepAsync(delay)
+
+  echo ""
+  echo "Done: ", nValid, " valid, ", nFail, " invalid  [", sessions.len, " total]"
+
+  if failures.len > 0:
+    echo ""
+    echo "Failures:"
+    for f in failures:
+      echo "  ", f
+
+waitFor main()