A 28,607-byte module executes decrypted code on import
The checked-in xscrape/fs.py is 42 lines long because most of its 28,607 bytes sit in dense numeric arrays. Read statically, the module reconstructs import names, decrypts an embedded byte sequence, decompresses it, and sends the result to builtins.exec. The call happens at module import. We did not execute or decode that hidden body outside the supplied sandbox, and its intent is not visible from the plain source.
xscrape/client.py imports that module and calls fs.run_sync() before defining XScrapeClient. This means a simple import reaches code created by the opaque payload. There is no scraping reason for a Python client to hide startup logic this way. Even if the concealed code turned out to be harmless, a reviewer cannot verify its behavior through normal source reading. That alone is enough for us to reject the package for real machines.
What happened when we ran it
Our run used commit 864851b in a fresh unprivileged Debian container with 3 CPUs, 8 GB of RAM, Python 3.12, and no secrets. Installation succeeded in 32 seconds, pulled 67 packages, and occupied 150 MB. The build finished successfully in 4 seconds. Pip-audit reported 0 known vulnerabilities in the dependency set it checked.
Pytest failed with exit 4 after 7 seconds while loading tests/conftest.py. Importing xscrape.auth first loads the package initializer, which imports XScrapeClient; client.py then reaches fs.run_sync(). The last exception is RuntimeError: win32 only. Collection ended there, so the log contains no passed or failed test count to report.
The audit result needs careful scope. Zero known dependency vulnerabilities says nothing about application code that decrypts and executes a bundled payload. Likewise, the successful 4-second build proves the package can be assembled, not that its client is safe or functional. The import failure is the useful result because it exposes startup behavior before any X credentials are supplied.
Version 0.4.2 still has a placeholder request path
In version 0.4.2, XScrapeClient._request() contains a comment calling the HTTP request a placeholder and returns an empty dictionary. _open() and _close() contain only ellipses. The search, timeline, and replies generators ask that empty response for a cursor, then stop without yielding a post. thread() calls the same request method and returns an empty list regardless of the response.
Those details conflict with a README that advertises search, profiles, threads, replies, media extraction, account rotation, adaptive rate limiting, and several storage targets. Some supporting pieces do exist. AuthPool tracks busy, invalid, and cooling accounts, while the CLI wires commands to JSON, CSV, and SQLite storage. Yet the network path connecting those pieces to X is absent in the checked-in client.
A user could see a successful CLI message with zero saved posts and mistake that for an empty search. The code gives no evidence that real GraphQL pagination currently happens. This is worse than a missing convenience feature because collection is the package's stated purpose. Fixing it calls for an implemented, reviewable request layer plus fixtures that exercise real response shapes.
Version 0.4.2 requires X session cookies
The version 0.4.2 configuration expects a cookie string containing auth_token and ct0, or a JSON file holding several accounts and their cookies. Requests are described as calls to X's public GraphQL web endpoints. That is different from using X's supported developer API, and the README warns users to follow platform rules and local law. Account operators must decide whether this access method is acceptable before supplying live sessions.
The account pool can rotate entries and mark them invalid or cooling, with defaults of 4 concurrent requests, a 20-second timeout, and 3 retries. None of that should be tested with real cookies while the import path is opaque. A disposable container without secrets limited our exposure, but a normal developer shell may hold SSH agents, cloud credentials, browser data, and source code.
The September 20 head has 255 stars and no release
GitHub showed 255 stars, 0 open issues or pull requests, and a last push on September 20, 2026. The latest-release endpoint returned no release, although pyproject.toml identifies version 0.4.2. An empty issue queue offers no public evidence of users reporting or resolving the import behavior. Recent code is a sign of activity, but activity does not answer the safety and completeness problems in this commit.
Skip xscrape. A useful rehabilitation would be easy to recognize: remove fs.py and every dynamically created startup action, implement the request methods in readable Python, publish a tagged release, and pass the suite on supported operating systems. Until then, the README's feature list is not a reason to accept opaque code or hand the client live X session cookies.

