Deterministic security checks for Next.js projects. No AI required.
next-secure-check helps developers find common security mistakes before they reach production: leaked secrets, unsafe API routes, missing rate limits, weak configuration, XSS risks, raw SQL patterns, unsafe upload endpoints, and missing security headers.
Current status: v0.3.0 release prep is complete. MVP, hardening, context-aware scanning, presets, AST-assisted rule refinements, SARIF polish, and CLI helper commands are complete.
Demo video planned.
Started on May 9, 2026.
Completed:
- CLI MVP
- 20 deterministic security rules
- 429 passing tests across packages and the web demo
- Terminal, JSON, Markdown, GitHub, and SARIF report formats
- Context metadata on findings
- CLI preset system for app-focused, strict, CI, audit, library, and monorepo scans
- Context-aware severity/confidence tuning to reduce noisy findings in large repositories
- AST-assisted checks for command execution, raw SQL interpolation, dangerous HTML rendering, and password handling
- Route-aware admin route detection and endpoint-aware upload validation
- Middleware-aware auth/rate-limit signals for common Next.js middleware patterns
- Regression fixture suite for representative monorepo, registry, tooling, XSS, SQL, password, admin, upload, and config noise cases
- Reduced
unknowncontext classifications for registry, story, demo, playground, fixture, and package UI paths - XSS sanitizer/source refinement for
dangerouslySetInnerHTML - Rate-limit detection refinement for route-level and middleware-level signals
- SARIF metadata polish with help URIs, CWE/security tags, security severity, context properties, and deterministic fingerprints
- CLI
rules,explain, andinithelper commands - GitHub Actions proof with Step Summary output
- Rule documentation in
docs/rules apps/webweb demo for scanning public GitHub repositories- GitHub repository URL validation, metadata check, tarball download, safe extraction, cleanup, API scan endpoint, report UI, exclude toggle, and JSON/Markdown export
- Phase 4.5+ web demo hardening, including repo size checks, server-side redaction, scan abuse guard, hardened security headers, orphan temp cleanup, optional GitHub token support, and hardened client IP parsing
- Pre-publish rule coverage polish for committed
.env.*variants and partial security header detection
Current release focus:
v0.3.x: improve real-project signal quality, document CLI workflows, and keep false-positive handling honest.The web demo scans public GitHub repositories using static analysis only. It does not run repository code, install dependencies, execute tests, or access private repositories.
The CLI is exercised in GitHub Actions as part of this repository's CI.
- The workflow runs the scanner with
--format githuband writes the markdown report to the job Step Summary via$GITHUB_STEP_SUMMARY. - When
--fail-on highis used, the job fails if any HIGH severity finding is reported. - When
--fail-on criticalis used, the job fails only when the scan summary risk level iscritical. - The step order is: build -> typecheck -> test -> security check, ensuring workspace packages are compiled before typechecking.
- The findings are deterministic pattern matches; no proof-of-exploit is executed.
AI-assisted development makes it easy to ship fast and miss security basics. This project focuses on checks that are evidence-based, understandable, and useful for junior developers, freelancers, small SaaS teams, and agencies.
This is a student-built learning project. I am developing it as a first-year computer programming student to improve my skills in TypeScript, Next.js security, static analysis, open-source project structure, testing, and product thinking.
The project was built with an AI-assisted workflow, including tools such as Codex, Claude, and ChatGPT for iteration, code review, testing strategy, and documentation support. The scanner itself does not use AI at runtime. It performs deterministic, rule-based static analysis.
AI helps with speed, structure, and feedback, but technical ownership, product direction, code review, testing, release decisions, and final responsibility remain mine.
I am still learning, so issues, feedback, and critical review are welcome. Findings should be treated as review signals rather than proof of exploitation, and false positives or false negatives are possible.
The scanner currently checks for 20 common security patterns. You can read more about each rule in the docs/rules directory.
- secrets/env-file-committed: Detects committed
.envfiles. - secrets/hardcoded-secret: Detects hardcoded API keys and tokens.
- secrets/weak-jwt-secret: Detects weak or default
JWT_SECRETvalues. - injection/no-eval: Detects
eval()usage. - injection/no-new-function: Detects
new Function()usage. - injection/command-exec: Detects shell command execution.
- xss/dangerously-set-inner-html: Detects raw HTML rendering in React.
- config/insecure-cors-wildcard: Detects wildcard CORS origins.
- auth/login-without-rate-limit: Detects login endpoints missing rate limiting.
- auth/register-without-rate-limit: Detects registration endpoints missing rate limiting.
- auth/password-without-hashing-library: Detects password handling without bcrypt/argon2.
- injection/raw-sql-concat: Detects raw SQL string interpolation.
- headers/missing-security-headers: Detects missing security headers in Next.js config.
- secrets/next-public-secret: Detects
NEXT_PUBLIC_secret-like variables. - upload/missing-file-type-validation: Detects upload endpoints missing file type validation.
- upload/missing-file-size-limit: Detects upload endpoints missing file size limits.
- validation/api-route-without-validation: Detects API routes that may be missing input validation.
- auth/admin-route-without-auth: Detects admin routes that may be missing authentication protection.
- config/production-browser-source-maps: Detects
productionBrowserSourceMaps: truein Next.js config. - config/next-powered-by-header: Detects missing
poweredByHeader: falsein Next.js config.
Recommended one-off usage:
npx --yes next-secure-check@latest scan . --preset appFor reproducible CI runs, pin the package version:
npx --yes next-secure-check@0.3.0 scan . --preset appInstall globally:
npm install -g next-secure-check
next-secure-check scan .If an older global install is present, unversioned npx next-secure-check can sometimes reuse the old binary and fail on v0.2 options such as --preset. Check and remove the global install when needed:
next-secure-check --version
npm list -g next-secure-check
npm uninstall -g next-secure-check
npm cache verifyOr run without a global install:
npx --yes next-secure-check@latest scan .
npx --yes next-secure-check@latest scan . --format json
npx --yes next-secure-check@latest scan . --format markdown --output report.md
npx --yes next-secure-check@latest scan . --format github
npx --yes next-secure-check@latest scan . --format sarif --output report.sarif
npx --yes next-secure-check@latest scan . --fail-on high
npx --yes next-secure-check@latest scan . --fail-on critical
npx --yes next-secure-check@latest scan . --category secrets,auth,xss
npx --yes next-secure-check@latest scan . --exclude "**/*.test.ts,examples/**"
npx --yes next-secure-check@latest scan . --preset app
npx --yes next-secure-check@latest scan . --preset strict
npx --yes next-secure-check@latest scan . --preset ci
npx --yes next-secure-check@latest rules
npx --yes next-secure-check@latest explain xss/dangerously-set-inner-html
npx --yes next-secure-check@latest init
node packages/cli/dist/index.js scan . --exclude "**/*.test.ts,examples/**"Prefer npx --yes next-secure-check@latest for local one-off scans, or pin next-secure-check@0.3.0 in CI.
List the built-in deterministic rules:
npx --yes next-secure-check@latest rulesExplain a single rule without opening the README:
npx --yes next-secure-check@latest explain xss/dangerously-set-inner-htmlCreate a starter config and GitHub Actions workflow:
npx --yes next-secure-check@latest initinit creates .next-secure-check.json and .github/workflows/next-secure-check.yml. Existing files are skipped by default. Use --force only when you intentionally want to overwrite those files:
npx --yes next-secure-check@latest init --force--fail-on critical is a risk-level gate. It exits with code 1 only when scan.summary.riskLevel is critical. --fail-on high, medium, low, and info continue to work as severity thresholds.
Presets adjust path coverage for common scan modes. They do not replace manual review; they help choose the right signal/noise tradeoff for the repository.
| Preset | Best for | Behavior |
|---|---|---|
default |
General local scans | Broad baseline coverage with standard context tuning |
app |
Production app-code sanity checks | Excludes tests, examples, docs, generated output, and GitHub workflow/tooling paths |
strict |
Aggressive review | Keeps tuning off and scans broadly |
ci |
Pull request checks | Excludes tests and generated output while keeping CI-oriented behavior practical |
audit |
Broad manual review | Scans broadly with tuning off for conservative review |
library |
Component/library repositories | Reduces docs/examples/test/generated noise |
monorepo |
Multi-app workspaces | App-focused coverage for apps/packages style repositories |
next-secure-check v0.3 is most useful as a quick review signal for application code. Large monorepos, template repositories, generator CLIs, release scripts, and demo/example-heavy repositories can still produce noise, but context metadata, presets, middleware signals, and AST-assisted rules make the default experience more practical.
For a production app-code-focused scan, you can start with:
npx --yes next-secure-check@latest scan . --preset appUse --preset strict when you want the more aggressive review mode. You can still add --exclude patterns when a repository has project-specific generated, demo, or fixture paths. Findings are focused pre-release review signals for common mistakes.
The CLI can read a local JSON config file named .next-secure-check.json from the scan target root.
Supported fields:
excludePaths: relative path glob patterns to ignorecategories: rule categories to runfailOn: severity threshold, orcriticalto gate on scan summary risk levelformat: report output formatpreset: scan preset (default,app,strict,ci,audit,library, ormonorepo)
Example:
{
"preset": "app",
"excludePaths": ["**/*.test.ts", "**/*.spec.tsx", "examples/**"],
"categories": ["secrets", "auth", "headers"],
"failOn": "high",
"format": "json"
}CLI flags always take priority over config values:
CLI flag > config file > defaultFor example, if the config file sets "format": "markdown" but the command uses --format json, the CLI prints JSON.
You can also point to an explicit config file:
npx --yes next-secure-check@latest scan . --config path/to/config.jsonThe web demo does not read .next-secure-check.json files from scanned repositories. Hosted/public scans use the web demo's own server-side options instead.
SARIF 2.1.0 output is available for tools such as GitHub Code Scanning:
npx --yes next-secure-check@latest scan . --format sarif --output report.sarifThe SARIF reporter includes:
- tool metadata with
informationUri - unique rule metadata with help URIs, tags, and selected CWE mappings
- result locations with file, line, and column when available
security-severityand precision metadata- deterministic
partialFingerprintsfor more stable result tracking - context and context reason properties for review triage
- concise result messages built from the finding title, description, and recommendation
Raw secret evidence is not included in SARIF output.
apps/
web/ local public-repository web demo app
packages/
core/ scanner orchestration, shared types, score engine
cli/ command line entrypoint
rules/ built-in rule modules
reporter/ terminal, JSON, markdown, GitHub, SARIF report output
examples/
vulnerable-next-app/
secure-next-app/
docs/
rules/pnpm install
pnpm build
pnpm typecheck
pnpm testThe root test command currently runs both package tests and web demo tests.
Expected current test coverage:
packages: 286 tests
apps/web: 143 tests
total: 429 testsAfter building, the CLI can be run locally:
node packages/cli/dist/index.js scan examples/vulnerable-next-app
node packages/cli/dist/index.js scan examples/vulnerable-next-app --format json
node packages/cli/dist/index.js scan examples/vulnerable-next-app --format markdown --output report.md
node packages/cli/dist/index.js scan examples/vulnerable-next-app --format github --fail-on high
node packages/cli/dist/index.js scan examples/vulnerable-next-app --format sarif --output report.sarif
node packages/cli/dist/index.js scan . --exclude "**/*.test.ts,examples/**"The web demo lives under apps/web. It is a local demo for scanning public GitHub repositories, not a production-ready hosted scanning service.
Current web demo flow:
Public GitHub repo URL
-> URL validation
-> public repository metadata check
-> tarball download
-> safe tarball extraction
-> core scanner
-> server-side evidence redaction
-> cleanup
-> report UI
-> optional test/example exclusion
-> JSON / Markdown exportThe web demo includes:
- public GitHub repository URL validation
- public repository metadata checks
- tarball download from GitHub with
User-Agent: next-secure-check - optional
GITHUB_TOKENsupport for higher GitHub API rate limits - safe tarball extraction with archive limits and path checks
- cleanup guarantee for extracted temporary files
- cleanup warning behavior when a scan succeeds but immediate cleanup fails
- core scanner integration
- server-side evidence redaction before results reach the browser
POST /api/scansbackend endpoint- UI scan flow with loading, error, and result states
- Exclude tests and examples toggle for cleaner production-like scans
- JSON and Markdown export actions
The web demo is intentionally limited.
It will not:
- access private repositories
- require login
- include payment
- run repository code
- run
npm install - run build, test, or package scripts from the scanned repository
- perform dynamic analysis
The goal is to scan public GitHub repositories using safe static analysis only. Secret-related evidence is redacted server-side for web responses and exports.
By default, the web demo can exclude test/spec files and examples/**:
**/*.test.ts
**/*.test.tsx
**/*.spec.ts
**/*.spec.tsx
examples/**Run the local web demo:
pnpm install
pnpm build
pnpm -C apps/web devThen open the local Next.js app and enter a public GitHub repository URL.
The scan API accepts:
POST /api/scans
Content-Type: application/json
{
"repoUrl": "https://github.com/owner/repo",
"excludePaths": ["**/*.test.ts", "examples/**"]
}A real local API smoke test passed with:
https://github.com/octocat/Hello-WorldThe web demo is designed for public, static scans only:
- public repositories only
- no scanned repository scripts are executed
- no dependency installation inside scanned repositories
- GitHub repository metadata and size checks before download
- safe tarball extraction with archive limits
- path traversal protection
- symlink and hardlink rejection
- duplicate archive path rejection
- server-side secret evidence redaction
- optional
GITHUB_TOKENsupport for GitHub metadata and tarball requests User-Agent: next-secure-checkon GitHub requests- optional Upstash Redis REST distributed scan guard
- in-memory IP rate limit and global concurrency guard fallback
- hardened client IP resolver with platform header priority, IPv4/IPv6 validation, and oversized header fallback
- GitHub request and scan-stage timeouts with safe error responses
- orphan temp cleanup for old scanner extraction directories
- successful scan results are still returned if immediate cleanup fails, with a safe cleanup warning
The in-memory scan guard is intended for the local/single-instance demo stage. Public multi-instance or serverless deployments should configure the distributed guard or equivalent platform-level protection.
- Rules are deterministic and combine lightweight pattern matching, syntax-level AST checks, and path/context signals, so they can still produce false positives and false negatives.
- Findings are review signals, not proof of exploitation.
- Large monorepos, demo/example-heavy repositories, template repositories, and tooling-focused repositories can still produce noisy findings.
- CLI generators and release/tooling scripts may trigger command execution findings even when that behavior is intentional for the tool.
- Full type-aware analysis is a v0.3 roadmap item.
- The in-memory scan guard is only a local/single-instance fallback.
- Public deployments should use the optional Upstash/distributed guard or equivalent platform-level protection.
- IP-based limits depend on trusted proxy/platform forwarded headers.
- The web demo scans public repositories only and does not run repository code.
Local terminal scans are manual: next-secure-check only runs when you execute the CLI. GitHub Actions scans are automatic after you add a workflow file to your repository; then GitHub runs the scan on the configured push or pull request events. The tool does not scan repositories on its own.
For a basic Step Summary workflow, copy this into .github/workflows/next-secure-check.yml:
name: next-secure-check
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Run next-secure-check
shell: bash
run: |
set -o pipefail
npx --yes next-secure-check@0.3.0 scan . --preset app --format github --fail-on high | tee -a "$GITHUB_STEP_SUMMARY"This fails the pull request when findings at HIGH or above are found. Change --fail-on to medium, low, or info if your team wants a stricter severity gate. Use --fail-on critical when you only want to fail on a critical scan summary risk level.
For GitHub Code Scanning, generate SARIF and upload it:
name: next-secure-check SARIF
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
security-events: write
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Run next-secure-check SARIF
run: npx --yes next-secure-check@0.3.0 scan . --preset app --format sarif --output next-secure-check.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: next-secure-check.sarifFindings are deterministic pattern matches, not proof of exploitation. Review the confidence, evidence, and recommendation fields before treating a finding as a confirmed vulnerability.
Manual validation notes:
Planned follow-up work:
- Optional type-aware analysis for rules that need deeper data-flow context.
- Deeper source analysis where syntax-level checks are not enough.
- More precise route graph and middleware matching.
- Custom rule configuration and rule enable/disable controls.
- Nonce/hash-based CSP hardening for the web demo.
- Public hosted demo hardening.
- More GitHub Code Scanning integration examples.
- Rule noise reduction and false positive tracking.
- VS Code extension or ESLint plugin exploration.
CLI works before web.
Tests pass before NPM release.
GitHub Action works before SaaS.
Real user feedback comes before payments.