{"id":51300806,"url":"https://github.com/osintph/suri","last_synced_at":"2026-06-30T19:30:59.021Z","repository":{"id":367955535,"uuid":"1282929714","full_name":"osintph/suri","owner":"osintph","description":"Web application security scanner for authorized VAPT engagements","archived":false,"fork":false,"pushed_at":"2026-06-28T11:42:30.000Z","size":54,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-28T13:20:09.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osintph.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-06-28T11:06:20.000Z","updated_at":"2026-06-28T11:42:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/osintph/suri","commit_stats":null,"previous_names":["osintph/suri"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/osintph/suri","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osintph%2Fsuri","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osintph%2Fsuri/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osintph%2Fsuri/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osintph%2Fsuri/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osintph","download_url":"https://codeload.github.com/osintph/suri/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osintph%2Fsuri/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34981389,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-06-30T19:30:55.003Z","updated_at":"2026-06-30T19:30:59.006Z","avatar_url":"https://github.com/osintph.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Suri\n\nWeb application security scanner for authorized VAPT engagements.\n\n[![CI](https://github.com/osintph/suri/actions/workflows/ci.yml/badge.svg)](https://github.com/osintph/suri/actions/workflows/ci.yml)\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)\n\n---\n\n## What Suri is\n\nSuri is a single static binary that crawls a web application, runs a suite of checks against the discovered surface, and writes findings to a SQLite database. It targets web applications, admin panels, REST APIs, and cloud storage. Every outbound request is validated against an engagement scope file before it is sent: if the host is not in scope, the request is blocked and logged. Findings are written in HTML and JSON formats suitable for client deliverables. A diff engine compares consecutive scans to show what changed between assessments.\n\n## What Suri is NOT\n\nSuri does not exploit vulnerabilities. Detection only: it reports that a parameter appears injectable but does not extract data or escalate access. It does not support authenticated sessions in v1 (no cookie injection, no CSRF token handling). It does not execute JavaScript and does not use a headless browser. All checks are safe to run against production systems during an authorized engagement window.\n\n---\n\n## Installation\n\n### macOS\n\nInstall via Homebrew:\n\n```bash\nbrew tap osintph/tap\nbrew trust osintph/tap\nbrew install suri\n```\n\nOn first run, macOS Gatekeeper will block the unsigned binary.\nClear the quarantine flag once:\n\n```bash\nsudo xattr -d com.apple.quarantine $(which suri)\n```\n\nVerify:\n\n```bash\nsuri --version\n```\n\nThese extra steps will go away in a future release once the binary\nis signed and notarized with an Apple Developer ID.\n\n### Linux\n\nDownload the appropriate binary from the\n[releases page](https://github.com/osintph/suri/releases) and extract:\n\n```bash\nwget https://github.com/osintph/suri/releases/download/v0.1.1/suri_0.1.1_linux_amd64.tar.gz\ntar xzf suri_0.1.1_linux_amd64.tar.gz\nsudo mv suri /usr/local/bin/\nsuri --version\n```\n\nOr install from source with Go 1.23+:\n\n```bash\ngo install github.com/osintph/suri/cmd/suri@latest\n```\n\n### Windows\n\nDownload the Windows binary from the\n[releases page](https://github.com/osintph/suri/releases),\nextract the zip, and add the directory to your PATH.\n\n### Build from source\n\n```bash\ngit clone git@github.com:osintph/suri.git\ncd suri\ngo build -o suri ./cmd/suri\n./suri --version\n```\n\nRequires Go 1.25 or later. No CGO dependencies.\n\n---\n\n## Quickstart\n\n**Step 1: write a scope file.**\n\n```toml\n# engagement.toml\nengagement_name = \"target-corp-2026-q3\"\nnotes           = \"Authorized VAPT. Contact: security@target.example.com\"\n\nhostnames = [\n  \"target.example.com\",\n  \"*.target.example.com\",\n]\n```\n\n**Step 2: run a scan.**\n\n```bash\n./suri scan --scope engagement.toml https://target.example.com\n```\n\nOutput at the end of the scan:\n\n```\nScan complete\n  URLs discovered:      47\n  Forms found:          3\n  Unique parameters:    12\n  JS artifacts:         8\n  Findings:             2 (info: 14 suppressed)\n  DB: /tmp/suri-out/a3f2c1d0-....db\n```\n\n**Step 3: generate a report.**\n\nCopy the scan ID from the `DB:` line (the UUID portion of the filename).\n\n```bash\n./suri report --scan a3f2c1d0-... --format html --out report.html\n```\n\nOpen `report.html` in a browser. For a machine-readable output:\n\n```bash\n./suri report --scan a3f2c1d0-... --format json --out report.json\n```\n\n---\n\n## Scope file format\n\nEach engagement gets its own scope file. The scope file is not a config file: it defines the legal boundary for the scan. Keep it outside version control alongside your findings.\n\n```toml\n# Required. Short identifier used in log output and report headings.\nengagement_name = \"acme-vapt-2026-q1\"\n\n# Optional free-form notes.\nnotes = \"Authorized VAPT for Acme Corp Q1 2026.\"\n\n# Hostnames in scope. Supports leftmost-label wildcard only:\n# *.example.com matches api.example.com but not example.com itself\n# and not sub.api.example.com.\nhostnames = [\n  \"example.com\",\n  \"*.example.com\",\n]\n\n# Individual IPs in scope.\nips = [\"203.0.113.10\"]\n\n# CIDR ranges in scope. Every IP within the range is allowed.\ncidrs = [\"10.10.0.0/24\"]\n\n# Port restriction. Empty list means all ports are in scope.\n# Port is derived from the URL scheme (80 for http, 443 for https)\n# unless an explicit port is present in the URL.\nports = [80, 443, 8080, 8443]\n\n# Cloud storage check authorization. Cloud checks refuse to run\n# unless the target bucket host matches an entry here.\n# Supports the same wildcard syntax as hostnames, plus * spanning\n# multiple labels (e.g. *.s3.*.amazonaws.com).\ncloud_buckets = [\n  \"*.s3.amazonaws.com\",\n  \"*.blob.core.windows.net\",\n  \"*.storage.googleapis.com\",\n]\n\n# Optional: custom endpoint for S3-compatible storage (Minio, Backblaze, etc).\n# The CLI --s3-endpoint flag takes precedence over this value.\ns3_endpoint    = \"\"\nazure_endpoint = \"\"\ngcs_endpoint   = \"\"\n```\n\n---\n\n## What Suri checks\n\n**Cloud storage**\n- S3 bucket exposure: anonymous list and read via virtual-hosted and path-style addressing\n- Azure Blob container anonymous access\n- Google Cloud Storage bucket anonymous access\n- Bucket name permutation from the target domain\n\n**Admin panel and sensitive path discovery**\n- Interesting paths (`.git/HEAD`, `.env`, `.htpasswd`, `wp-config.php`, etc.): content-verified against known patterns; soft-200 SPA responses are not flagged\n- Common admin paths: wordlist-based, results reported as info/tentative unless access is denied (401, 403)\n\n**API discovery**\n- Swagger and OpenAPI spec discovery; parses found specs and inventories endpoints\n- GraphQL introspection: flags open introspection as a finding\n\n**Web injection**\n- Reflected XSS: canary-based payload with context detection (HTML, attribute, JS, URL contexts)\n- SQL injection: error-based (DB error string detection) and time-based (sleep payload with baseline comparison)\n- Server-side template injection: canary expression evaluation for Jinja2, Twig, Freemarker, ERB\n- Command injection: time-based sleep payloads only\n- Open redirect: canary URL injection\n\n**Security headers**\n- CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy\n\n**Backup and source exposure**\n- `.git/HEAD`, `.env`, swap files, `.DS_Store`, source maps, common backup extensions\n- Content-verified; SPA catch-all responses are filtered by body hash deduplication\n\n---\n\n## Output formats\n\nBy default the scan summary shows findings at medium severity or above. Info-severity findings are written to the database but suppressed from the summary line.\n\n```bash\n# Show info findings in the summary\nsuri scan --scope engagement.toml --include-info https://target.example.com\n```\n\nGenerate reports from any past scan using its ID:\n\n```bash\nsuri report --scan \u003cid\u003e --format html --out report.html\nsuri report --scan \u003cid\u003e --format json --out report.json\n```\n\nThe `--db` flag overrides the default database lookup (most recent `.db` in the current directory):\n\n```bash\nsuri report --scan \u003cid\u003e --db /path/to/scans.db --format html --out report.html\n```\n\nHTML reports are self-contained single files with inline CSS. No external resources. A Content-Security-Policy meta tag prevents execution of any script content found in evidence.\n\nJSON reports include base64-encoded request and response evidence and are suitable for ingestion by other tooling.\n\n---\n\n## Diff engine\n\nRun a second scan after remediations and compare:\n\n```bash\n# First scan\nsuri scan --scope engagement.toml https://target.example.com\n# Note scan ID printed at end: abc123...\n\n# Re-scan after remediation\nsuri scan --scope engagement.toml https://target.example.com\n# Note new scan ID: def456...\n\n# Diff report\nsuri diff --baseline abc123... --current def456... --format html --out diff.html\n```\n\nThe diff report groups findings into:\n- **New**: appeared in the current scan but not in the baseline\n- **Persistent**: present in both scans (not yet remediated)\n- **Resolved**: present in the baseline but absent from the current scan\n\n---\n\n## Polite scanning principles\n\nSuri is designed for authorized assessments against production systems.\n\n**Rate limiting.** The default is 10 requests per second per host. Override with `--rate`.\n\n**Scan timeout.** The default is 15 minutes. The scan stops cleanly at the limit and writes all findings collected up to that point. Override with `--scan-timeout`. Exit status 124 indicates a timeout.\n\n**Serialised timing probes.** SQL injection and command injection timing checks use sleep-based payloads. Only one sleep payload is in-flight against any single host at a time, so the checks cannot exhaust backend thread pools. Probes against different hosts run in parallel.\n\n**Content verification.** Admin path discovery and backup file checks verify response body content before emitting findings, filtering out SPA catch-all 200 responses.\n\n---\n\n## Legal disclaimer\n\n**Suri is for authorized use only.**\n\nRunning Suri against systems you do not own or do not have explicit written permission to test is illegal in most jurisdictions and is a violation of the AGPL license under which Suri is distributed. Every scan requires a scope file that declares the engagement. The scope file is a record that you have identified the legal boundary of your assessment. If you are unsure whether you have authorization, do not run Suri.\n\n---\n\n## Contributing\n\nBug reports and feature requests: [github.com/osintph/suri/issues](https://github.com/osintph/suri/issues).\n\nSuri is licensed under the AGPL-3.0. Contributions must be made under the same license. By submitting a pull request you agree that your contribution is licensed to the project under AGPL-3.0.\n\nSee [WORDLISTS.md](WORDLISTS.md) for wordlist attribution and licensing.\n\n---\n\n## License\n\nSuri is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3. See [LICENSE](LICENSE) for the full text.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosintph%2Fsuri","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosintph%2Fsuri","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosintph%2Fsuri/lists"}