{"id":47730707,"url":"https://github.com/jcouture/ghostscan","last_synced_at":"2026-04-02T21:27:30.916Z","repository":{"id":346582806,"uuid":"1182814116","full_name":"jcouture/ghostscan","owner":"jcouture","description":"Detect hidden Unicode attacks in source code before they fool you.","archived":false,"fork":false,"pushed_at":"2026-03-24T23:59:56.000Z","size":159,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T17:27:06.939Z","etag":null,"topics":["cli","golang","security","static-analysis","unicode"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcouture.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-03-16T01:31:18.000Z","updated_at":"2026-03-24T23:57:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jcouture/ghostscan","commit_stats":null,"previous_names":["jcouture/ghostscan"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/jcouture/ghostscan","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouture%2Fghostscan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouture%2Fghostscan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouture%2Fghostscan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouture%2Fghostscan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcouture","download_url":"https://codeload.github.com/jcouture/ghostscan/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouture%2Fghostscan/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31316745,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["cli","golang","security","static-analysis","unicode"],"created_at":"2026-04-02T21:27:30.292Z","updated_at":"2026-04-02T21:27:30.909Z","avatar_url":"https://github.com/jcouture.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ghostscan\n\n\u003e Static Unicode security scanner for developers and CI teams reviewing untrusted source code.\n\n## Overview\n\nIt is built for security engineers, maintainers, Go developers, and DevOps teams who need a fast, local, deterministic check before code lands in CI, a release, or a dependency tree. Instead of trying to be a general SAST platform, it focuses narrowly on Unicode-based deception: hidden characters, misleading script mixing, payload-like sequences, and nearby decode-or-execute patterns. The differentiator is simple: it makes invisible evidence readable and keeps the output precise enough for code review and CI decisions.\n\n```bash\n~\u003e ghostscan --verbose ./testdata/invisible/single.txt\n\n             ########\n         ###        ###\n       ##             ##\n       ##   ##   ##    ##\n       #    ##   ##    ##\n       #               ##\n      ##     #####     ##\n     ##                 ###\n    ##                    ##\n    ## ###             #####\n         ##           ##\n           ###         #\n              ###########\n\nghostscan v0.2.0\n\nFinding:     Invisible unicode character\nEvidence:    \u003cU+200B ZERO WIDTH SPACE\u003e\nRuleID:      unicode/invisible\nFile:        /Users/johnsmith/ghostscan/testdata/invisible/single.txt\nLine:        1\nColumn:      2\nCount:       1 suspicious runes\nCategory:    invisible unicode\nContext:\n  A\u003cU+200B ZERO WIDTH SPACE\u003eB\nFingerprint: /Users/johnsmith/ghostscan/testdata/invisible/single.txt:unicode/invisible:1:2\n\n8:57PM INF scanned 1 files (6 B) in 123µs\n8:57PM INF skipped 0 files (none)\n```\n\n## Features\n\n- **Visible evidence for invisible content**: Renders hidden Unicode as strings like `\u003cU+200B ZERO WIDTH SPACE\u003e`.\n- **Focused Unicode threat coverage**: Detects invisible characters, private-use Unicode, bidi controls, directional marks, mixed-script tokens, and combining marks.\n- **Payload-aware heuristics**: Flags long hidden sequences, dense suspicious regions, decoder markers, and payload-plus-decoder correlations.\n- **Safe repository traversal**: Skips symlinks, NUL-containing files, oversize files, and common dependency or build directories.\n- **CI-friendly behavior**: Uses deterministic ordering, plain-text output, and exit codes `0`, `1`, and `2`.\n\n## Installation\n\n```bash\n# Pre-built release binary\n# Download the archive for your platform from:\n# https://github.com/jcouture/ghostscan/releases\n# Then extract it and place `ghostscan` on your PATH\n\n# From source\ngit clone https://github.com/jcouture/ghostscan.git\ncd ghostscan\ngo mod download\ngo run . --version\n\n# Build a local binary\nmake build\n./bin/ghostscan --help\n\n# Go install\ngo install github.com/jcouture/ghostscan@latest\nghostscan --version\n```\n\n\u003e **Requirements:** Go `1.26.1` is pinned in `go.mod` and `mise.toml` for source builds. Pre-built release archives are produced for Linux, macOS, and Windows.\n\nYou should see `ghostscan dev (commit none)` from a plain source build, or a real tag and commit in a release build.\n\n## Usage\n\n```text\nghostscan [flags] [path]\n\nFlags:\n      --exclude strings     exclude files or directories matching this glob; repeatable\n      --max-file-size int   skip files larger than this many bytes\n  -n, --no-color            disable color\n      --no-default-excludes disable built-in exclude globs\n      --silent              suppress the startup banner\n      --verbose             print detailed structured finding blocks\n  -v, --version             print version\n```\n\n### Common Examples\n\n```bash\n# Scan the current repository\nghostscan .\n\n# Scan a specific directory\nghostscan ./testdata/mixed\n\n# Scan a single file\nghostscan ./testdata/invisible/single.txt\n\n# CI-friendly output\nghostscan --silent --no-color .\n\n# Show detailed findings\nghostscan --silent --no-color --verbose ./testdata/mixed/correlated_decoder_near_payload.js\n\n# Add repeatable exclude globs\nghostscan . --exclude \"**/*.min.js\" --exclude \"vendor/**\"\n\n# Disable built-in excludes and use only an explicit glob\nghostscan . --no-default-excludes --exclude \"**/*.gen.js\"\n\n# Enforce a smaller max file size\nghostscan --max-file-size 1048576 .\n```\n\n## Output and Exit Codes\n\n`ghostscan` prints a human-readable terminal report. In verbose mode, each finding includes:\n\n- file path\n- line and column\n- evidence with invisible Unicode rendered visibly\n- local context\n- rule ID\n- fingerprint\n\nVerbose mode also reports exclusions during traversal:\n\n```text\nSKIP dist/app.min.js (matched exclude: \"**/*.min.js\")\nSKIP vendor (matched exclude: \"vendor/**\")\n```\n\nExit codes:\n\n| Exit code | Description                                                  |\n|-----------|--------------------------------------------------------------|\n| 0         | scan completed and found no suspicious patterns              |\n| 1         | scan completed and found suspicious patterns                 |\n| 2         | execution failed because of invalid input or another runtime |\n\n## Scan Behavior\n\nThe current scanner behavior is intentionally narrow and real:\n\n- Recursively scans a file or directory path.\n- Does not follow symlinks.\n- Treats files containing a NUL byte as binary and skips them.\n- Uses a default max file size of `5 MiB`.\n- Matches excludes against the full normalized relative path with `/` separators.\n- Supports repeatable `--exclude` globs with `**` matching zero or more path segments and `filepath.Match` semantics for other segments.\n- Applies built-in excludes by default: `.git/**`, `node_modules/**`, `vendor/**`, `dist/**`, `build/**`, `target/**`, `out/**`, and `coverage/**`.\n- `--no-default-excludes` disables the built-in exclude set completely.\n- Never executes scanned code or fetches network resources.\n\n## FAQ\n\n**I downloaded `ghostscan` on macOS and it is blocked by Gatekeeper. What should I do?**\n\nRemove the quarantine attribute from the binary:\n\n```bash\nxattr -d com.apple.quarantine ghostscan\n```\n\n**Does `ghostscan` run or decode the code it scans?**\n\nNo. It only performs static checks on file contents.\n\n**Can I scan a single file instead of a whole repository?**\n\nYes. Pass the file path directly to `ghostscan`.\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcouture%2Fghostscan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcouture%2Fghostscan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcouture%2Fghostscan/lists"}