Security First Aid is a deterministic, local-first security scanner for repositories, GitHub Actions workflows, Dockerfiles, and common application configuration.
It is built for open-source maintainers, solo developers, and small teams that need one thing from security tooling: show the risky mistakes, explain why they matter, and tell them what to fix first.
- Why this exists
- How it works
- What is implemented
- Quick start
- Install options
- Example output
- How to make it work with npm and npx
- Usage
- Repository policy
- GitHub Action
- Rule coverage
- Development
- Project structure
- Documentation map
- Troubleshooting
- License
Most security tools miss the target for smaller teams:
- they are too shallow and produce lint-like warnings without context
- they are too noisy and bury real risk under hundreds of findings
- they assume the user already knows how to interpret security jargon
Security First Aid is intentionally opinionated:
- deterministic first
- local-first by default
- no outbound telemetry in the core scanner
- high-signal checks over broad, noisy coverage
- plain-English remediation over vague compliance language
flowchart LR
A[Discover files] --> B[Classify artifacts]
B --> C[Run deterministic rules]
C --> D[Generate findings]
D --> E[Apply baseline and policy]
E --> F[Render terminal, JSON, Markdown, or SARIF]
At a high level:
- Security First Aid walks the target repository and discovers security-relevant files.
- It classifies those files into known artifact types such as workflow YAML, Dockerfiles,
.envfiles, and JSON config. - It runs narrow deterministic rules against those artifacts.
- It normalizes the findings into a stable schema.
- It applies repository policy and baseline suppression.
- It renders the results in human-readable or machine-readable formats.
The current codebase includes:
- a production-grade Node.js CLI
- recursive artifact discovery
- deterministic rule execution
- repository policy loading from
.sfa.json - baseline creation and baseline suppression
- terminal, JSON, Markdown, and SARIF reporters
- fixture-backed tests with Node's built-in test runner
- a reusable GitHub Action wrapper
The current v1 rule pack implements 15 deterministic checks across:
- secrets exposure
- committed environment files
- GitHub Actions permissions and trigger misuse
- workflow pipe-to-shell behavior
- Docker privilege and image hygiene basics
- debug mode leakage
- insecure CORS, cookies, and session configuration
Run the CLI directly from the repository:
npx security-first-aid@latest scan . --format terminalOr install it globally:
npm install -g security-first-aid
sfa scan . --format terminalRecommended first run after install:
npm install -g security-first-aid@latest
sfaThen run one of these:
sfa scan . --format terminal
sfa rules list --format json
sfa baseline create . --output ./.sfa-baseline.jsonImportant: npm may not always show lifecycle output clearly during npm install -g, depending on the user's npm configuration. The reliable built-in guide is sfa or sfa --help.
If you just want the built-in guide:
npx security-first-aid@latestor:
sfa --helpYou can still run the CLI directly from the repository:
node ./src/cli/index.js scan . --format terminalGenerate a Markdown report:
node ./src/cli/index.js scan . --format markdown --output ./reports/security-report.mdCreate a baseline from current findings:
node ./src/cli/index.js baseline create . --output ./.sfa-baseline.jsonList the available rules:
node ./src/cli/index.js rules list --format jsonnpm install -g security-first-aid@latestThen immediately run:
sfaThen choose a command:
sfa scan . --format terminal
sfa rules list --format jsonnpx security-first-aid@latest scan . --format terminalTo show the built-in quick-start guide instead of running a scan:
npx security-first-aid@latestThis is the simplest option while developing or testing locally:
node ./src/cli/index.js scan . --format terminalFrom the repository root:
npm linkThat exposes the binary defined in package.json:
sfa scan . --format terminal
sfa rules list --format jsonCreate a distributable package:
npm packInstall that tarball globally:
npm install -g ./security-first-aid-0.1.2.tgzThen run:
sfa scan . --format terminalReal Markdown report excerpt captured from the project:
Generate a comparable report from any repository with:
sfa scan . --format markdown --output ./reports/security-report.mdTerminal output example from the bundled insecure fixture:
Security First Aid
Target: ./tests/fixtures/insecure-service
Findings: 15
High: 6 Medium: 7 Low: 2
[HIGH] SFA_SECRET_001 .env - Environment variable SECRET_KEY appears to contain a hardcoded secret.
[HIGH] SFA_GHA_001 .github/workflows/deploy.yml - GitHub Actions workflow grants write-all permissions.
[HIGH] SFA_GHA_003 .github/workflows/deploy.yml - Workflow is triggered by pull_request_target.
[HIGH] SFA_GHA_004 .github/workflows/deploy.yml - Workflow uses curl piped to bash.
[HIGH] SFA_CONFIG_003 config.json - Session cookie configuration disables the secure flag.
[HIGH] SFA_CONFIG_005 config.json - Configuration combines wildcard CORS origins with credentialed requests.
[MEDIUM] SFA_ENV_001 .env - Repository contains a `.env` file rather than only a template or example.
The package metadata needed for npm and npx is already in place:
- package name:
security-first-aid - executable binary:
sfa - entry point:
./src/cli/index.js - publishable file whitelist:
src,CHANGELOG.md,README.md,LICENSE
The package is now published to the npm registry as security-first-aid.
Future releases can be shipped through GitHub Actions instead of repeating the manual publish sequence.
Automated release path:
- update
package.jsonto the target version - add the matching release section to
CHANGELOG.md - commit the release changes
- push a tag like
v0.1.2
The Release workflow will:
- validate version and changelog consistency
- run the release verification suite
- build a tarball and SHA-256 checksum
- publish to npm using the repository
NPM_TOKENsecret - create a GitHub release using the matching changelog notes
Required GitHub secret:
NPM_TOKEN
Verify the packed contents:
npm pack --dry-runLog in to npm:
npm loginIf the unscoped name security-first-aid is available, publish it:
npm publishIf the name is already taken, switch to a scoped name such as @your-org/security-first-aid in package.json, then publish it publicly:
npm publish --access publicUsers can install it globally with npm:
npm install -g security-first-aidOr run it directly with npx:
npx security-first-aid@latest scan . --format terminalIf you publish under a scope, use the scoped package name instead:
npx @your-org/security-first-aid@latest scan . --format terminalCurrent published version:
security-first-aid@0.1.2
sfa scan <target-path> [options]Supported options:
--format json|markdown|sarif|terminal--baseline <path>--config <path>--severity-threshold low|medium|high|critical--output <path>
Examples:
sfa scan . --format terminal
sfa scan . --format json --severity-threshold medium
sfa scan . --format sarif --baseline ./.sfa-baseline.json
sfa scan . --format markdown --output ./reports/report.mdsfa baseline create <target-path> [options]Examples:
sfa baseline create .
sfa baseline create . --output ./.sfa-baseline.jsonsfa rules list [--format json|markdown|terminal]Examples:
sfa rules list
sfa rules list --format json
sfa --helpSecurity First Aid automatically looks for a .sfa.json file in the scan target root unless you pass --config.
Example:
{
"enabledRules": ["SFA_SECRET_001", "SFA_GHA_003"],
"disabledRules": ["SFA_CONFIG_001"],
"severityThreshold": "high",
"baselinePath": "./.sfa-baseline.json"
}Supported fields:
enabledRulesdisabledRulesseverityThresholdbaselinePath
Precedence:
- CLI flags
.sfa.json- built-in defaults
This repository includes a reusable composite action at .github/actions/run-sfa/action.yml.
Minimal example:
name: Security Scan
on:
pull_request:
push:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: ./.github/actions/run-sfa
with:
target-path: .
format: terminal
severity-threshold: highSupported action inputs:
target-pathformatseverity-thresholdbaseline
SFA_SECRET_001hardcoded secret in environment fileSFA_ENV_001live.envfile present in repository
SFA_GHA_001workflow useswrite-allpermissionsSFA_GHA_002workflow action pinned to moving refSFA_GHA_003workflow usespull_request_targetSFA_GHA_004workflow pipes remote content into a shellSFA_GHA_005workflow omits explicit permissions block
SFA_DOCKER_001Dockerfile missing non-root userSFA_DOCKER_002Dockerfile uses floatinglatesttagSFA_DOCKER_003Dockerfile usesADDinstead ofCOPY
SFA_CONFIG_001debug mode enabled in JSON configSFA_CONFIG_002wildcard CORS origin in JSON configSFA_CONFIG_003session cookie secure flag disabledSFA_CONFIG_004session cookie httpOnly flag disabledSFA_CONFIG_005wildcard CORS combined with credentials
- Node.js 22 or newer
- npm
This repository currently has no external runtime dependencies.
node --test --experimental-test-isolation=nonenode scripts/smoke.mjsnpm test
npm run check
npm run scan:fixture
npm run release:validate
npm run pack:check
npm run release:checksrc/
application/ scan orchestration
cli/ CLI entry points and argument parsing
domain/ findings, policy, and rule catalog logic
infrastructure/ filesystem and artifact discovery
reporters/ terminal, JSON, Markdown, and SARIF output
rules/ deterministic security checks
tests/
application/ orchestration tests
cli/ CLI behavior tests
reporters/ reporter tests
fixtures/ secure and insecure sample repositories
docs/
reference/ CLI and configuration reference
sop/ operating procedures
adr/ architecture decisions
- PRD.md - product requirements and v1 scope
- ARCHITECTURE.md - system architecture and trust boundaries
- ROADMAP.md - phased delivery plan
- SECURITY.md - disclosure and secure development policy
- CONTRIBUTING.md - contributor workflow and quality gates
- docs/reference/cli.md - CLI command reference
- docs/reference/configuration.md -
.sfa.jsonformat - docs/testing-strategy.md - test strategy
- AGENTS.md - operating instructions for AI coding agents and contributors
If you linked or installed the package globally and sfa is not available:
npm linkOr reinstall the tarball/global package:
npm install -g ./security-first-aid-0.1.2.tgzPowerShell may prefer a generated sfa.ps1 shim. If that shim is blocked by execution policy, use:
sfa.cmd scan . --format terminalOr:
cmd /c sfa scan . --format terminalThat only works after the package is published to npm. Before publishing, use one of these:
node ./src/cli/index.js ...npm linkthensfa ...npm packthennpm install -g ./security-first-aid-0.1.2.tgz
