Airsec README

The project's README from github.com/bas3line/Airsec. As one markdown file: README.md.

airsec

State-of-the-art security review, driven by the coding CLI you already have.

Claude Code · Codex · OpenCode · Command Code

ci license: MIT node

No new API key. No cloud upload. No third-party scanner. airsec drives the agent already installed on your machine through a structured, multi-pass security audit, then turns the result into a report you can act on.


Quick start

npx airsec@latest init

That is the whole thing. init walks you through it: pick the folder, pick your coding CLI, pick the model, choose review-or-fix, and it runs.

Other ways to install
npm  install -g airsec      # global
pnpm add    -g airsec
bun  add    -g airsec
yarn global add airsec

airsec init                 # then run it anywhere

No curl | sh installer, deliberately: a tool that audits your code for supply-chain risk should not ask you to pipe a URL into a shell.

Getting ETARGET · No matching version found?

Your npm is pointed at a mirror that has not synced the release yet. Check with npm config get registry — if it is anything other than https://registry.npmjs.org, either wait for the mirror to catch up or bypass it:

npx --registry=https://registry.npmjs.org airsec@latest init   # one-off
npm config set registry https://registry.npmjs.org             # permanently

Requirements

60 seconds, end to end

npx airsec harness            # 1. what coding CLIs can airsec see?
npx airsec@latest init        # 2. guided setup — writes .airsec/config.json, then runs
npx airsec report --format html > report.html   # 3. share the result
npx airsec fix --severity high                  # 4. remediate the worst of it

Why this instead of a scanner

Pattern scanners find what patterns catch. An agent that reads your code can follow a tainted value through three helpers and a middleware layer and tell you whether the sink is actually reachable. airsec supplies the missing part: a rigorous process around that agent.

Supported coding CLIs

Run airsec harness to see which of these are installed and what models each offers.

Harness id Install
Claude Code claude npm i -g @anthropic-ai/claude-code
Codex codex npm i -g @openai/codex
OpenCode opencode npm i -g opencode-ai
Command Code commandcode npm i -g command-code
Gemini CLI gemini npm i -g @google/gemini-cli — experimental adapter
Cursor Agent cursor curl https://cursor.com/install -fsS | bash — experimental adapter
Crush crush npm i -g @charmland/crush — experimental adapter

The first four are built against headless flags verified against the real CLIs, and each is tested end to end in both directions — audit (read-only) and fix (write). The ones marked experimental are best-effort: they are overridable from config without touching the source.

Models are discovered, not hardcoded

Model ids change every few months. airsec never ships a versioned id that can rot into a wrong default, so airsec harness --models <id> asks your machine instead:

Harness Where the list comes from
OpenCode opencode models — the live list, hundreds of entries
Command Code cmd --list-models — the live list with descriptions
Claude Code your ~/.claude/settings.json model, plus the opus/sonnet/haiku aliases, which resolve to the current model by design
Codex the model in your ~/.codex/config.toml, including profiles

A probe command is only ever one verified to list models. Guessing is unsafe: several of these CLIs treat an unrecognised subcommand as a prompt, so a wrong guess would bill you for a model call instead of printing a list. Where no list exists, the wizard offers "harness default" plus free-text entry — always correct, never stale.

Usage

Guided

npx airsec init          # asks: which folder, which CLI, which model, review or fix
npx airsec init ../api   # start on a specific path

The wizard writes .airsec/config.json, so every later run is a bare airsec scan.

Non-interactive

airsec scan                                      # uses .airsec/config.json, or sensible defaults
airsec scan ../api --depth paranoid
airsec scan --harness codex --model gpt-5.1-codex-max
airsec scan --modules injection,authn-authz,secrets --min-severity medium
airsec scan --quiet --reports sarif --fail-on high --require-complete   # CI shape

Exit codes only matter when something automated is watching — a CI pipeline decides pass or fail from this number. Running airsec yourself, you can ignore them; the wizard does not ask about them for that reason.

Exit Meaning
0 No finding at or above --fail-on
1 airsec itself failed — bad config, harness missing, unreadable target
2 A finding at or above --fail-on (default high) survived verification
3 An audit pass did not finish, and --require-complete was set

--fail-on sets the bar: --fail-on critical fails a build only on critical findings, --fail-on none never fails it. It changes nothing about the scan or the report.

Coverage can be partial, and that is not the same as clean. If a harness invocation times out or errors, airsec records it, finishes the remaining passes, and reports the run — but the areas that pass covered were never reviewed. Both the terminal output and report.md lead with a warning when this happens, summary.incompletePasses carries it in JSON, and --require-complete turns it into a build failure. Never read a partial run as a clean bill of health.

Parallel sessions of the same coding CLI contend for one rate limit, and a stalled session burns the full --timeout (default 900s) before giving up. Concurrency 5 against Claude Code lost 5 of 14 passes to timeouts in testing; the default of 3 is deliberately conservative. If you see timeouts, lower --concurrency before raising --timeout.

Fixing

airsec fix                        # pick findings from the last review interactively
airsec fix --all --severity high  # remediate everything high and above

Fixes are applied to your working tree, one finding at a time — sequentially on purpose, because parallel agents editing overlapping files produce conflicting writes. Review the diff before you commit; airsec never commits for you.

Each fix is scoped to one finding, so a change that alters a function's contract can leave a caller in another file out of step. The remediation prompt tells the agent to follow the change across file boundaries, but weaker models do not always manage it — run your tests after fix, always.

Reports

Every run is stored under .airsec/runs/<timestamp>-<id>/ with the formats you configured.

airsec report                        # re-print the last review
airsec report --format html > out.html
airsec report --format sarif > out.sarif

SARIF uploads straight to GitHub code scanning.

Depth

Depth Lenses per module Verifiers per finding File budget
quick 1 0 600
standard 1 1 2,000
deep (default) 2 2 6,000
paranoid 3 3 20,000

Cost scales roughly with modules × lenses, plus one verification call per finding per verifier. Start at quick on a new codebase to calibrate before committing to paranoid.

Audit modules

airsec modules prints these with their CWE and OWASP mappings.

Core (default): injection · authn-authz · secrets · crypto · input-validation · ssrf-network · xss-frontend · path-file · supply-chain · infra-config

Extended (opt in): api-surface · logic-race · llm-agentic · privacy-compliance

Configuration

.airsec/config.json:

{
  "version": 1,
  "target": "/abs/path/to/repo",
  "harness": "claude",
  "model": "opus",
  "depth": "deep",
  "modules": ["injection", "authn-authz", "secrets"],
  "concurrency": 3,
  "timeoutMs": 900000,
  "retries": 1,
  "verify": true,
  "minSeverity": "low",
  "exclude": ["fixtures/", "generated/"],
  "reports": ["markdown", "json", "sarif"],
  "failOn": "high",
  "harnesses": {}
}

exclude entries are case-insensitive substring matches against each path relative to the target — not globs. "generated/" drops the directory; ".test." drops test files anywhere.

Overriding a harness

When a CLI changes its headless flags, fix it in config rather than waiting for a release. Tokens {{prompt}}, {{system}}, {{model}} and {{cwd}} are substituted per invocation; an argument whose token has no value is dropped along with its flag.

{
  "harnesses": {
    "gemini": {
      "bin": "gemini",
      "promptVia": "stdin",
      "args": {
        "read": ["--model", "{{model}}", "--output-format", "text"],
        "write": ["--model", "{{model}}", "--yolo"]
      }
    }
  }
}

How a run works

inventory   walk the tree, detect stack, rank security-relevant paths   (deterministic)
    ↓
recon       one pass: entrypoints, trust boundaries, sensitive assets   (1 call)
    ↓
audit       modules × lenses, run concurrently                          (m × l calls)
    ↓
merge       fingerprint by location, corroborate by title overlap       (deterministic)
    ↓
verify      independent reviewers try to refute each finding            (f × v calls)
    ↓
score       severity × confidence × verdict × corroboration             (deterministic)
    ↓
report      terminal · markdown · json · sarif · html

Development

bun install
bun run dev -- scan ./fixture --depth quick   # run from source
bun run check                                 # lint + typecheck + test, what CI runs
bun run format                                # apply Biome fixes
bun run build                                 # bundle to dist/cli.js, node-compatible

Bun is the toolchain; the published bundle targets Node ≥ 20 so npx airsec works anywhere. Biome enforces formatting and lint, tsc --strict enforces types, and CI runs all three plus the packaged binary on every push.

Extending it. A new harness is one file in src/harness/adapters/ plus a line in registry.ts — or zero code, via a config override. A new audit module is one entry in src/audit/catalog.ts. A new report format is one entry in src/report/index.ts.

src/
  audit/      what to look for — module catalog, search lenses, prompts, output schema
  core/       the pipeline — inventory, recon, fan-out, merge, verify, score, trust boundary
  harness/    how to talk to each coding CLI — adapters, process exec, JSON extraction
  report/     terminal · markdown · json · sarif · html
  ui/         wizard prompts, progress, banner
  util/       fs, json recovery, hashing, redaction, logging

Security model

airsec is pointed at code you may not trust, so it treats the target directory as hostile input.

Config from the target is not trusted to decide what runs. .airsec/config.json lives inside the directory under review. Its harnesses block names an executable and its argv, so honouring it from a repository you just cloned would be remote code execution. That block is ignored by default, with a warning, and applied only when you pass --trust-config on the command line — somewhere the repository cannot reach. Everything else in the file only decides what gets read, and is applied normally.

Audit passes cannot execute commands or reach the network. The repository under review is attacker-controlled input to a model, so injected instructions in a README or a code comment must not be able to reach a shell (remote code execution) or the network (exfiltration of the source being reviewed). Reconnaissance, auditing and verification run with only the tools an audit needs — open a file, search, list.

For Claude Code that means Bash, WebFetch, WebSearch and Task are denied alongside the editing tools; Task because a subagent could otherwise be spawned with the tools just removed. Codex runs under --sandbox read-only, OpenCode under its plan agent, Command Code under --permission-mode plan. Write access is granted only by airsec fix, and even then network tools stay denied.

This is verified rather than assumed: a fixture repository carrying prompt-injection text in its README and source is scanned as part of testing, and the injected command does not run.

Discovered credentials are masked in stored output. A secrets finding would otherwise copy a live credential into report.md, report.json and report.sarif — files that get committed and pasted into tickets. High-entropy tokens are masked to sk_l…redacted…4c across every text field of a finding, not just the code snippet: models routinely spell the same credential out again in the exploit narrative as a URL parameter. The credential stays identifiable, and the file, line, and exploit path are untouched. Add .airsec/ to your .gitignore regardless.

These properties are covered by test/security.test.ts.

Caveats

License

MIT

Airsec (bas3line/Airsec)

Searching Database…

150 ·GitHub
这是什么?Airsec 是一个 CLI 工具,驱动你已安装的编程智能体(Claude Code、Codex、OpenCode、Command Code)对代码进行结构化、多遍的安全审计,然后把发现整理成可付诸行动的报告。
这是传统的基于模式的扫描器吗?不是——它不匹配模式,而是把你的代码交给一个智能体,后者能追踪被污染的值穿过辅助函数和中间件的过程,判断危险汇点(sink)是否真正可达;整个流程包含严格的侦察、14 个审计模块和对抗性验证。
我怎么开始使用它?运行 npx airsec init——向导会引导你选择目标文件夹、编程 CLI、模型以及审查或修复模式,并写入配置,之后的每次运行只需 airsec scan。
它需要哪些编程 CLI,我必须额外安装什么吗?你需要 Node 20+,以及 PATH 中已有的至少一个受支持的编程 CLI——Claude Code、Codex、OpenCode 和 Command Code 经过完整测试,Gemini CLI、Cursor Agent 和 Crush 为实验性适配。已发布的 airsec 包本身零运行时依赖。
扫描可能不完整或超时吗?可能——框架调用可能超时(默认 900 秒),卡住的会话会在放弃前耗尽全部超时时间;测试中对 Claude Code 使用并发 5 时,14 个遍次中丢了 5 个,因此默认并发为 3。未完成的遍次会在报告和 summary.incompletePasses 中标记,部分完成的运行绝不应被解读为干净通过。
我的代码或 API 密钥会被发送到任何地方吗?不会——airsec 完全通过你机器上已有的编程 CLI 运行,不需要新的 API 密钥,不上传云端,也不涉及任何第三方扫描器。
它在我代码中发现的密钥会被怎么处理?发现的凭据会在发现项的每个文本字段(report.md、.json、.sarif,甚至漏洞利用叙述)中被掩码成类似 skl...redacted...4c 的形式,不过仓库仍建议把 .airsec/ 加入 .gitignore。
airsec 能自动修复它发现的问题吗?可以,airsec fix 会按顺序逐个发现项地把修复应用到你的工作树,以避免相互冲突的修改。你仍应检查 diff 并运行测试,因为只针对单个文件的修复可能让别处的调用方失去同步。
扫描能深入到什么程度?四个预设——quick、standard、deep(默认)和 paranoid——会调整每个模块的透镜数量、每个发现的验证器数量和文件预算,从 quick 的 600 个文件且无验证,到 paranoid 的 20,000 个文件且三重验证。
把它指向我不信任的克隆仓库安全吗?安全——目标目录被视为敌意输入:它自己的 .airsec/config.json 中的任何框架覆盖配置都会被忽略,除非你传入 --trust-config;审计遍次在没有 shell 和网络工具的情况下运行,并已针对提示注入测试夹具做过验证。
What are similar projects?deepsec, Myrqen, shannon, rea, semgrep, cve-lite-cli, SkillSpector, raptor
Creator
bas3line
Size
0.2 MB
Stars
150
Updated
CommitAug 15
Age
2mo
Commits · 90 Days