GapScan developer docs
GapScan measures software against the standard it answers to and reports every unmet control as a gap, with evidence. The first standard is application security: OWASP ASVS 5.0, Top 10:2025, API Top 10, MASVS and the Top 10 for LLM apps.
These docs cover the open-source engine you can run today on your own machine. The cloud service is in development; the parts that depend on it are marked.
Quick start
You need uv, the scanners GapScan wraps, and Ollama for the local AI review. gapscan doctor tells you what is missing.
$ make setup $ uv run gapscan doctor $ uv run gapscan data fetch # once, needs network $ ollama pull qwen3-coder:30b $ uv run gapscan serve --open # local dashboard
Then scan a project, review the findings and write the report:
$ gapscan scan ./my-project -o scan.json $ gapscan triage scan.json -t ./my-project --deep-dive $ gapscan report scan.json --triage triage.json
data fetch is the one network step. It downloads security data from pinned hosts and checks every file against its checksum. [add the public repository link]
How a scan works
Each step hands its output to the next. A step that fails is reported in the result, never hidden.
Semgrep with GapScan rules, OSV-Scanner, Trivy, Gitleaks, Checkov, Bandit, or any SARIF file.
One finding format, duplicates merged.
Each finding tied to a control in the target standard.
Request data followed across files to the sink.
AI review with redacted context, labeled as AI.
Coverage, gaps, evidence and limits.
Commands
| Command | What it does |
|---|---|
| gapscan doctor | Checks that the scanners, the model and the security data are ready. |
| gapscan data fetch | Downloads security data once, from pinned hosts, with checksums. |
| gapscan scan <path> -o scan.json | Runs the scanners and writes normalized findings. Makes no network call. |
| gapscan triage scan.json -t <path> | AI review of each finding. Add --deep-dive for a slower, deeper pass. |
| gapscan report scan.json --triage triage.json | Writes the report with coverage, gaps and AI verdicts. |
| gapscan run --ai-review --profile <name> | Scan, review and report in one step. |
| gapscan fix | Proposes a fix, re-scans it, and offers it only when the re-scan is clean. |
| gapscan serve --open | Opens the local dashboard, with the coverage page per standard. |
| gapscan eval run --targets <set> | Runs the evaluation harness on a labeled set, such as benchmark-java. |
| gapscan eval compare | Compares two evaluation runs. |
Run gapscan <command> --help for every option.
Gaps and statuses
For each control in the target standard, GapScan gives one of three statuses. None of them is “met”: a person decides what passes.
The evidence shows the control is not met, for example a request parameter reaching a shell command.
The control can be checked by code, and nothing was found.
The control asks for documents, processes or run-time behaviour that code cannot show.
Every gap carries the control ID, the CWE, the file and line, the data-flow trace where there is one, the AI verdict and the fix. See the sample report.
AI review
The AI review reads each finding with its surrounding code and decides whether it is real. It removes false alarms; it does not find new bugs.
- Local by default. The model runs through Ollama on your machine or an internal host. qwen3-coder:30b is the tested model. The smallest profile,
tiny, is not recommended: it confirms almost everything. - Labeled. Every verdict is marked as AI output, with its reasoning, so a reviewer can check it.
- Checked. Answers must match a fixed format, and deterministic rules run after the model and are never overridden.
- Guarded. Text in the code that tries to instruct the reviewer is detected. A dismissal of such a finding is never accepted; it goes to a person.
- Memories. Your team’s past decisions can be given to the model as context. A dismissal made with a memory shown is never final for high or critical findings.
Configuration
Copy config/gapscan.example.toml and edit it. Two settings people change first:
[scanners] packs = ["security"] # only listed packs load [scanners.semgrep] rules_path = "./my-rules" # add your own Semgrep rules
GapScan ships its own Apache-2.0 rule library. The Semgrep community rules are not bundled, because their license forbids use in a competing product. You can opt in for internal use only.
CI, IDE and agents
- CI: run a scan in any pipeline and stop the build when it finds new gaps.
- Pull requests: gaps posted as review comments on the changed lines.
- Pre-commit: a hook that scans staged files before each commit.
- VS Code: an extension, shipped as a VSIX, that shows gaps in the editor.
- MCP: a server so coding agents can scan and check their own changes.
Setup steps for each are in docs/ci.md in the repository.
Team server
A self-hosted server for a team: a finding lifecycle, SLA metrics, a REST API and Postgres storage, with Jira, ServiceNow and Slack integrations. It ships as a Docker image and a Helm chart. See docs/team-server.md.
Offline and air-gapped
Offline bundles carry the engine, rules and security data to a machine with no network. Updates arrive as verified bundles. In an air-gapped image, scan, report and doctor work fully offline; triage needs a model endpoint on localhost or an internal host, for example Ollama beside the container. See docs/offline.md.
Languages
371 rules in 9 languages. Scores are catch rate minus false-alarm rate on the held-out half of each benchmark, against the community rules for the same language.
| Language | Data flow across files | GapScan | Community rules |
|---|---|---|---|
| Go | Per function | +86 | +16 |
| C# | Per function | +75 | +14 |
| JavaScript / TypeScript | Yes | +61 | +42 |
| Python | Yes | +58 | +20 |
| Java | Yes (+62 with the data-flow engine) | +47 | +48 |
| Swift | Per function | 69% caught | 6% caught |
| Kotlin | Per function | 65% caught | 0% caught |
| PHP | Per function | +8 | 0 |
| Dart / Flutter | Per function | 7% caught | 0% caught |
Dart and PHP coverage is thin, and real iOS apps are much harder than the demo apps behind the Swift score. Full tables are in docs/rules.md.
Hard rules
These hold in every mode and every release.
- Scanned code is never built or run.
- Nothing leaves the machine during a scan. The only network step is the data download at setup.
- Secrets are redacted before anything is stored, shown or sent to a model.
- AI output is always labeled as AI, with its reasoning.
- GapScan never says a product is compliant or certified. It reports coverage and gaps.
Evaluation
Rules are tuned on one half of each benchmark and scored once on the other half. AI review dismissed under 5% of real bugs on every test set (3 of 110 on the held-out Java set). Run the harness yourself:
$ gapscan eval run --targets benchmark-java $ gapscan eval compare
Method, confidence intervals and limits are in docs/evaluation.md.
Coming in the cloud
- Coverage view per standard across all projects.
- Signed proof of fix: a record that a re-scan confirmed the gap is closed.
- Verdict network: opt-in sharing of redacted fingerprints, never source code.
- Regression radar: re-checks every project when a new advisory lands.
- Dynamic evidence: import OWASP ZAP and Nuclei results, for example with
gapscan scan --dynamic-report FILE. - More standards: NIST SSDF, ISO 27001, SOC 2 and PCI DSS first, then EU, Gulf and Egyptian frameworks with Arabic reports.