{"id":48439918,"url":"https://github.com/plarix-security/wyscan","last_synced_at":"2026-04-10T11:03:21.837Z","repository":{"id":345891924,"uuid":"1162550790","full_name":"plarix-security/wyscan","owner":"plarix-security","description":"Static analysis scanner that detects Agent Failure Boundary exposures in agentic AI codebases.","archived":false,"fork":false,"pushed_at":"2026-04-06T13:19:17.000Z","size":1190,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-06T13:25:12.769Z","etag":null,"topics":["failure-detection","scanner","security"],"latest_commit_sha":null,"homepage":"https://www.plarix.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plarix-security.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-20T11:59:53.000Z","updated_at":"2026-04-06T13:19:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/plarix-security/wyscan","commit_stats":null,"previous_names":["plarix-security/agent-failure-boundary-scanner","plarix-security/argus"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/plarix-security/wyscan","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plarix-security%2Fwyscan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plarix-security%2Fwyscan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plarix-security%2Fwyscan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plarix-security%2Fwyscan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plarix-security","download_url":"https://codeload.github.com/plarix-security/wyscan/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plarix-security%2Fwyscan/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31477013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T14:34:32.243Z","status":"ssl_error","status_checked_at":"2026-04-06T14:34:31.723Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["failure-detection","scanner","security"],"created_at":"2026-04-06T15:01:28.730Z","updated_at":"2026-04-10T11:03:21.829Z","avatar_url":"https://github.com/plarix-security.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca href=\"https://plarix.dev\"\u003eWyScan\u003c/a\u003e\n\nStatic scanner for Python, TypeScript, and JavaScript agent code that reports reachable operations from detected tool registrations.\n\nRuntime: CLI scanner plus GitHub App webhook backend. No HTML/CSS frontend is shipped.\n\nWyScan is an AFB04 scanner that parses Python, TypeScript, and JavaScript with tree-sitter, resolves tool registrations semantically when the code structure allows it, traces reachable calls across the analyzed file set, and reports matched operations when no credited structural policy gate is detected in that analyzed path.\n\n```bash\nwyscan scan ./agent-project\n```\n\nExample output:\n\n```text\nwyscan v1.5.8  ·  Plarix\n\nScanning  agent-project\n\n-----------------------------------------------------\n1 critical  ·  1 warning\n-----------------------------------------------------\n\nCRITICAL  tools/setup.py:60\nshutil.rmtree(agent_dir)\nDetected reachable call from tool registration to shutil.rmtree. No policy gate detected in the analyzed call path.\n\nWARNING   tools/api.py:42\nrequests.post(url, data)\nDetected reachable call from tool registration to requests.post. No policy gate detected in the analyzed call path.\n```\n\n## Scope\n\nWyScan currently does all of the following:\n\n- Parses Python and TypeScript/JavaScript source with tree-sitter.\n- Resolves tool registrations from framework-specific code structure through semantic analysis.\n- Matches reachable operations such as shell execution, file mutation, file reads, HTTP requests, email sends, and common database calls.\n- Suppresses findings when a structural policy gate is detected in the analyzed path.\n- Carries supporting evidence such as traced input flow, resource hints, and structural versus semantic registration evidence into CEEs.\n\nWyScan currently does not do any of the following:\n\n- Execute your code.\n- Trace into third-party package internals.\n- Detect AFB01, AFB02, or AFB03.\n\n## Quick Start\n\n```bash\ngit clone https://github.com/plarix-security/wyscan.git\ncd wyscan\nnpm install\nnpm run build\nnpm link\n\nwyscan scan ./my-agent-project\n\n# one-step CLI setup\nwyscan install\n```\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `wyscan install` | Run install/build/link/check and print setup dashboard |\n| `wyscan scan \u003cpath\u003e` | Scan a directory or file |\n| `wyscan check` | Verify local scanner dependencies |\n| `wyscan version` | Print the package version |\n| `wyscan help` | Show CLI help |\n\n## Scan Flags\n\n| Flag | Short | Current behavior |\n|------|-------|------------------|\n| `--level \u003cseverity\u003e` | `-l` | Filters detailed findings by `critical`, `warning`, or `info` |\n| `--output \u003cfile\u003e` | `-o` | Writes terminal or JSON output to a file |\n| `--json` | `-j` | Emits structured JSON |\n| `--summary` | `-s` | Prints a one-line summary |\n| `--quiet` | `-q` | Suppresses output and relies on exit code |\n| `--debug` | `-d` | Prints thrown errors during failures |\n\n## Exit Codes\n\n| Code | Current meaning |\n|------|-----------------|\n| 0 | No critical or warning findings were reported |\n| 1 | At least one warning finding was reported and no critical finding was reported |\n| 2 | At least one critical finding was reported |\n| 3 | Scanner initialization failed, the target path was invalid, or one or more files failed to analyze |\n\nExit codes follow the filtered report level. For example, `--level critical` ignores warning-only results when computing the exit code.\n\n## Detection Model\n\nWyScan reports matched operations reachable from detected tool registrations.\n\nSeverity is determined by operation category:\n\n- Irreversible operations such as `subprocess.run`, `eval`, `exec`, and file deletion are `CRITICAL`.\n- State-modifying operations such as file writes and HTTP POST requests are `WARNING`.\n- Read-only operations such as file reads and HTTP GET requests are `INFO`.\n\n### Operation Categories\n\n`CRITICAL`\n\n- Shell execution such as `subprocess.run`, `subprocess.Popen`, `os.system`, and similar process spawns\n- Dynamic code execution such as `eval`, `exec`, unsafe deserialization, and native library loading\n- File or directory deletion such as `shutil.rmtree`, `os.remove`, or `.unlink()`\n- `cursor.executescript()` and Redis flush operations\n\n`WARNING`\n\n- File writes and directory creation such as `.write_text()`, `.mkdir()`, `shutil.copy()`, and `open(..., \"w\")`\n- HTTP requests that match write-style calls such as `requests.post()` and `httpx.patch()`\n- HTTP GET calls currently matched by the shipped detector, such as `requests.get()` and `httpx.get()`\n- Common database write-style calls such as `.save()`, `.create()`, `.commit()`, Mongo insert or update calls, and email sends\n\n`INFO`\n\n- File reads such as `.read_text()`, `open(..., \"r\")`, and directory listing or glob operations\n- Generic database reads such as `.execute()`, `.fetchone()`, `.fetchall()`, `.query()`, and Redis `get` or `keys`\n\n## Framework Labels\n\nFramework detection supports both Python and TypeScript/JavaScript. WyScan uses semantic extraction from framework code structure such as decorator imports, tool lists, helper-returned tool bundles, `create_react_agent(...)`, `bind_tools(...)`, and `function_map` style registrations.\n\n### Python Frameworks\n\n- LangChain/LangGraph\n- CrewAI\n- AutoGen\n- OpenAI tool schemas and dispatch maps\n- LlamaIndex\n- MCP\n- Framework-core structural decorator registrations\n\n### TypeScript/JavaScript Frameworks\n\n- OpenAI SDK\n- LangChain.js/LangGraph.js\n- Vercel AI SDK\n- MCP SDK\n- Mastra\n- Playwright/Puppeteer browser automation\n- Framework-core structural registrations only (no generic exported-entry fallback)\n\nFramework labels are derived from structural analysis, not pattern tables.\n\n## Policy Gate Detection\n\nWyScan credits a policy gate only when the analyzed path contains code it recognizes as a structural authorization gate.\n\nCurrent gate credit comes from:\n\n- A function with conditional branches that check parameters and raise an authorization-like exception\n- Decorators that are structurally analyzed as wrappers that can prevent execution\n- Imported decorators whose wrapper logic is structurally analyzed as gates\n\nCurrent non-gates:\n\n- Logging calls\n- `try/except` around an operation\n- Generic exceptions that do not look authorization-related\n- Decorator or function naming alone without structural proof\n\n## JSON Output\n\n```bash\nwyscan scan ./project --json\n```\n\nCurrent CLI JSON shape:\n\n```json\n{\n  \"version\": \"1.5.8\",\n  \"scanned_path\": \"/absolute/path\",\n  \"files_analyzed\": 34,\n  \"runtime_ms\": 1200,\n  \"total_cees\": 10,\n  \"findings\": [\n    {\n      \"severity\": \"CRITICAL\",\n      \"file\": \"/absolute/path/tools/setup.py\",\n      \"line\": 60,\n      \"column\": 5,\n      \"operation\": \"File system operation: shutil.rmtree\",\n      \"tool\": \"setup_agent\",\n      \"framework\": \"langchain\",\n      \"tool_file\": \"/absolute/path/tools/setup.py\",\n      \"tool_line\": 12,\n      \"call_path\": [\n        \"/absolute/path/tools/setup.py:setup_agent\",\n        \"/absolute/path/helpers/files.py:delete_workspace\"\n      ],\n       \"involves_cross_file\": true,\n       \"unresolved_calls\": [],\n       \"depth_limit_hit\": false,\n       \"evidence_kind\": \"semantic\",\n       \"supporting_evidence\": [\n         \"Tool registration evidence: decorator tool imported from langchain.tools\",\n         \"Operation evidence: resolved module alias shutil\"\n       ],\n       \"resource\": \"agent_dir\",\n       \"changes_state\": true,\n       \"description\": \"Detected reachable call from tool \\\"setup_agent\\\" (langchain) to shutil.rmtree through 1 intermediate call(s). No policy gate detected in the analyzed call path.\",\n       \"code_snippet\": \"shutil.rmtree(agent_dir)\",\n       \"category\": \"file_operation\"\n    }\n  ],\n  \"cees\": [\n    {\n      \"severity\": \"CRITICAL\",\n      \"file\": \"/absolute/path/tools/setup.py\",\n      \"line\": 60,\n      \"column\": 5,\n      \"operation\": \"File system operation: shutil.rmtree\",\n      \"tool\": \"setup_agent\",\n      \"framework\": \"langchain\",\n      \"tool_file\": \"/absolute/path/tools/setup.py\",\n      \"tool_line\": 12,\n      \"call_path\": [\n        \"/absolute/path/tools/setup.py:setup_agent\",\n        \"/absolute/path/helpers/files.py:delete_workspace\"\n      ],\n      \"gate_status\": \"absent\",\n      \"afb_type\": \"AFB04\",\n       \"classification_note\": \"Detected reachable call from tool \\\"setup_agent\\\" (langchain) to shutil.rmtree through 1 intermediate call(s). The analyzed path crosses file boundaries. No policy gate detected in the analyzed call path.\",\n       \"involves_cross_file\": true,\n       \"unresolved_calls\": [],\n       \"depth_limit_hit\": false,\n       \"evidence_kind\": \"semantic\",\n       \"supporting_evidence\": [\n         \"Tool registration evidence: decorator tool imported from langchain.tools\",\n         \"Operation evidence: resolved module alias shutil\"\n       ],\n       \"resource\": \"agent_dir\",\n       \"changes_state\": true,\n       \"code_snippet\": \"shutil.rmtree(agent_dir)\",\n       \"category\": \"file_operation\"\n    }\n  ],\n  \"summary\": {\n    \"cees\": 10,\n    \"critical\": 2,\n    \"warning\": 3,\n    \"info\": 5,\n    \"suppressed\": 0\n  },\n  \"coverage\": {\n    \"languages_scanned\": [\"python\", \"typescript\", \"javascript\"],\n    \"languages_skipped\": [],\n    \"frameworks_detected\": [\"langchain\", \"openai\"],\n    \"files_analyzed\": 34,\n    \"files_skipped\": 0,\n    \"failed_files\": [],\n    \"skipped_files\": [],\n    \"partial\": false,\n    \"total_files_discovered\": 34,\n    \"file_limit\": 9007199254740991,\n    \"file_limit_hit\": false,\n      \"limitations\": [\n        \"The analyzed file set is graphed together per language. Changed-file scans do not guarantee full repository coverage.\"\n      ]\n  },\n  \"methodology\": {\n    \"short_answer\": \"No. WyScan is on the right track, but it is not yet ready to guarantee full CEE coverage for large-scale agentic systems such as Eliza.\",\n    \"regex_main_method\": false,\n    \"primary_method\": \"Tree-sitter AST parsing plus semantic tool-registration and call-path analysis.\",\n    \"large_scale_agentic_readiness\": {\n      \"ready\": false,\n      \"verdict\": \"Not nearly ready for complete coverage of all CEEs in large-scale agentic systems.\"\n    },\n    \"plan_for_large_scale_cee_coverage\": [\n      \"Build and maintain a versioned large-scale benchmark corpus (including Eliza-style repositories) with hand-labeled CEEs.\",\n      \"Add whole-repository symbol and module resolution for dynamic exports/imports and cross-file indirection.\",\n      \"Add deeper interprocedural dataflow and alias tracking from tool inputs to execution sinks.\",\n      \"Improve dynamic registration recovery (config-driven/plugin-based/runtime-composed tools) with explicit uncertainty reporting.\",\n      \"Replace remaining regex sink matching paths with typed semantic sink models where possible; keep regex as fallback only.\",\n      \"Gate releases on benchmark precision/recall targets and publish those metrics in CI artifacts and release notes.\"\n    ]\n  }\n}\n```\n\nNotes:\n\n- `files_analyzed` at the top level is the report file count used by the current CLI output.\n- `coverage.files_analyzed` counts all analyzed files across languages.\n- `cees` is the full detected execution-event inventory for the analyzed path set.\n- `findings` is the AFB04 subset of `cees`.\n- `call_path` lists the traced function path in the analyzed file set.\n- `evidence_kind`, `supporting_evidence`, `resource`, and `changes_state` expose the current proof and action summary for each finding or CEE.\n- `methodology` includes a blunt readiness verdict, whether regex is the main method, and the concrete plan for large-scale CEE coverage.\n- The repository does not currently promise a separately versioned stable JSON schema.\n\n## GitHub App\n\nWyScan can run as a GitHub App for supported webhook events.\n\nCurrent behavior:\n\n- Push events run only on the repository default branch.\n- Pull request events run on `opened` and `synchronize`.\n- Push scans the repository working tree.\n- Pull request scans changed analyzable files.\n- Findings appear in a GitHub Check.\n- Pull requests receive an updated issue comment only when findings are present.\n\nFailure and partial-coverage reporting are being tightened further in the implementation. For current details, see `docs/github-app.md` and the CLI output itself.\n\n## GitHub App Deployment\n\n### Quick Start\n\n1. **Build Docker Image**\n```bash\ndocker build -t wyscan-github-app .\n```\n\n2. **Run Container**\n```bash\ndocker run -d \\\n  --name wyscan-app \\\n  -p 3000:3000 \\\n  -e GITHUB_APP_ID=your-app-id \\\n  -e GITHUB_PRIVATE_KEY=\"$(cat private-key.pem)\" \\\n  -e GITHUB_WEBHOOK_SECRET=your-webhook-secret \\\n  wyscan-github-app\n```\n\n### Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `GITHUB_APP_ID` | Yes | GitHub App ID from app settings page |\n| `GITHUB_PRIVATE_KEY` | Yes | Private key PEM content (use newlines as `\\n` in env) |\n| `GITHUB_WEBHOOK_SECRET` | Yes | Secret for webhook signature verification |\n| `PORT` | No | Server port (default: 3000) |\n\n### How It Works\n\nWhen the Wyscan GitHub App is installed on a repository:\n\n1. **Installation Event** (`installation.created`)\n   - Automatically scans all repositories in the installation\n   - Posts a security issue to each repo with AFB04 scan results\n   - Creates and applies \"security\" label\n\n2. **Push Events** (default branch only)\n   - Scans the entire repository at the new commit\n   - Creates a GitHub Check run\n   - Posts a new security issue with findings\n\n3. **Pull Request Events** (`opened`, `synchronize`)\n   - Scans only changed files\n   - Creates a GitHub Check run\n   - Posts/updates PR comment with findings\n\n### Issue Format\n\nSecurity issues are titled: `[Wyscan] Security Scan Report — \u003ccommit_sha\u003e`\n\nEach issue contains:\n- **Summary table** with files analyzed, CEEs detected, findings by severity\n- **Detailed findings** organized by severity (Critical, Warning, Info)\n- **Coverage notes** explaining analysis scope and limitations\n- **Terminal-style formatting** with dividers and emoji severity indicators\n\n### Health Check\n\n```bash\ncurl http://localhost:3000/health\n# Response: {\"status\":\"ok\"}\n```\n\n### Logs\n\n```bash\ndocker logs -f wyscan-app\n```\n\nFor complete setup instructions including GitHub App registration, see `docs/GITHUB_APP_SETUP.md`.\n\n## Current Limitations\n\n- Directory scans graph the analyzed file set together per language. Changed-file scans still do not guarantee full repository coverage.\n- External packages are not traced.\n- Dynamic runtime tool registration is not guaranteed to be detected.\n- Framework labels and sink matching are not complete runtime framework models and are not guaranteed to cover all CEEs in large-scale systems.\n- Validation-helper severity downgrades are heuristic only.\n- AFB01, AFB02, and AFB03 are not implemented.\n- Rust support is not yet implemented.\n\n## Development\n\n```bash\nnpm install\nnpm run build\nnpm test\nnpm run wyscan -- scan ./project\n```\n\n## Documentation\n\n- `docs/install.md`\n- `docs/usage.md`\n- `docs/detection.md`\n- `docs/github-app.md`\n- `docs/limitations.md`\n\n## License\n\nApache-2.0. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplarix-security%2Fwyscan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplarix-security%2Fwyscan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplarix-security%2Fwyscan/lists"}