Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevSec Scanner

A local-first security scanner: run SAST, SCA, IaC, DAST, secret, container-image and supply-chain scans from one CLI — or a native desktop app — normalize every tool's output into one schema, and explore the results in a React dashboard.

Everything runs on your machine via Docker — no SaaS, no CI minutes required. A reference GitHub Actions workflow is included to show how the same scans fit into a pipeline when you want them there.

Why

Security scans take effort, so they often just don't get run:

  • The tools are fragmented. Semgrep, Trivy, Checkov, OSV-Scanner and ZAP each have their own CLI, flags and JSON shape, and just reading five different report formats is a chore.
  • The dashboards that unify them want your code. Most tools that do aggregate results are SaaS: you upload your source, pay per seat, and burn CI minutes to get a view.
  • Results end up scattered. Even after you run everything, the findings sit in five terminal dumps with no shared severity scale, no history, and no single place to triage them.

The goal here is to make a full local security pass one command (or one click) away. Every scanner sits behind one interface, every result is normalized into one schema, and one dashboard shows them all — on your machine, with your source and results never leaving it. Point it at a folder, a URL, or a container image, and read the results in one place. The CLI fits into CI; the desktop app runs the same scans without a terminal.

日本語版 README はこちら → · GitHub

Screenshots

Results (summary + findings) Scan (folder / URL / image)
Results Scan
History (trend + past runs) Scan Guide
History Scan Guide
Finding detail Japanese UI (i18n)
Finding detail Japanese UI
Settings (Socket.dev key) Scan in progress (toggles + cancel)
Settings Scanning

What gets scanned

Category Full name What it checks Tool
SAST Static Application Security Testing Source code, statically, for vulnerable patterns Semgrep
SCA Software Composition Analysis Dependencies for known CVEs Trivy fs
IaC Infrastructure as Code Misconfigurations in infra definitions Checkov
DAST Dynamic Application Security Testing A running app, by attacking it OWASP ZAP
Secret Secret scanning Hardcoded credentials, tokens and keys Trivy
Image Container image scanning OS and package vulnerabilities in a built image Trivy image
Supply chain Supply-chain analysis Dependencies vs the OSV vulnerability database OSV-Scanner
Supply risk Supply-chain risk Install scripts & unpinned deps (local); behavioral analysis with a key Socket.dev / local

A filesystem scan (make scan) runs SAST + SCA + IaC + secret + supply-chain + supply-risk together; DAST (--url) and container-image (--image) scans are run on demand. Each scanner's native JSON is normalized into a single Finding shape and aggregated into one scan-report.json that the dashboard reads.

Static vs dynamic

  • Everything except DAST is static. SAST, SCA, IaC, secret, container-image and supply-chain scans read your source, dependencies, infra config or image without running anything — no traffic is sent to the target, the app does not need to be running, and they never execute the scanned code, so there are no side effects on it.
  • DAST is dynamic. It drives a running app and sends real requests. This finds runtime issues but can have side effects on the target, so only scan systems you own or are authorized to test.

DAST: baseline vs active

This project runs the ZAP baseline (passive) scan: it spiders the site and inspects the responses (missing security headers, cookie flags, info disclosure). Impact is low, so it is reasonable even against production.

A full / active scan (not run here by default) injects attack payloads (SQLi, XSS, …) to confirm exploitable issues. It is far more invasive — it can create or delete data, send mail, or load the target — so reserve it for staging or systems you own, and prefer the authenticated header (--auth-header) only against test environments.

Architecture

pnpm workspace
├── packages/core   # Zod schema + types for the unified report (shared)
├── packages/cli    # Orchestrates the Docker-based scanners, writes the report
└── packages/web    # React + Vite dashboard that visualizes the report

See docs/architecture.md for the data flow, the swappable parts (seams), and the supported running modes (local CLI, web viewer, desktop app, and the planned hosted mode).

Prerequisites

  • Node.js ≥ 20 and pnpm ≥ 9
  • Docker (Desktop or Engine) running locally — the scanners run as containers

On Docker Desktop for Mac/Windows, --network=host is unavailable. To scan a locally running app with DAST, target http://host.docker.internal:<port> instead of localhost.

Quick start

pnpm install          # install workspace dependencies
cp .env.example .env  # adjust scan target / output path if needed

make scan             # run the static suite (SAST + SCA + IaC + secret + supply)
make dev              # open the dashboard (http://localhost:5174)

make scan writes the unified report to the path in SCAN_REPORT_PATH (default packages/web/public/scan-report.json), which the dashboard loads on startup. A sample report ships in the repo so the dashboard has data on first run.

Commands

Run scanners individually or all together:

make scan          # SAST + SCA + IaC + secret + supply-chain (the static suite)
make scan-sast     # Semgrep only
make scan-sca      # Trivy only (dependency CVEs + secrets)
make scan-iac      # Checkov only (skipped automatically when no *.tf files exist)
make scan-supply   # OSV-Scanner only (supply-chain)
make scan-socket   # supply-chain risk (local; Socket.dev with SOCKET_API_KEY)
make scan-dast     # OWASP ZAP baseline — requires a target URL
make scan-image IMAGE=nginx:1.25   # Trivy container-image scan

make scan runs the full static suite; scan-dast (needs a URL) and scan-image (needs an image) are opt-in.

DAST is opt-in because it needs a running target:

DAST_TARGET_URL=http://host.docker.internal:3000 make scan-dast

DAST also supports multiple comma-separated targets and scanning behind auth (the header is injected into every request via ZAP's replacer):

pnpm --filter @devsec/cli scan -- --only dast \
  --url http://host.docker.internal:3000,http://host.docker.internal:8080 \
  --auth-header "Bearer eyJhbGci..."

Want to see it surface real findings? A deliberately insecure target ships in examples/vulnerable-app:

make scan SCAN_TARGET_DIR=examples/vulnerable-app

Development & quality:

make dev         # Vite dev server for the dashboard
make build       # build all packages
make lint        # ESLint + Prettier check
make test        # Vitest unit/integration suite (no Docker needed)
make integration # Real-scanner tests via Docker (Semgrep/Trivy/Checkov)
make e2e         # Playwright browser tests
make typecheck   # tsc --noEmit across packages
make help        # list all targets

The CLI also accepts flags directly:

pnpm --filter @devsec/cli scan -- \
  --target ./my-project \
  --only sast,sca \
  --out ./scan-results/report.json \
  --url http://host.docker.internal:3000

# scan a container image
pnpm --filter @devsec/cli scan -- --only image --image nginx:1.25

Selectable --only types: sast, sca, iac, dast, image, supply, socket (secrets are emitted by the sca scan). The socket scan adds Socket.dev behavioral analysis when SOCKET_API_KEY is set (in the desktop app, via Settings); otherwise it runs local heuristics only.

Verify it locally

A quick loop to see the whole thing working end to end:

  1. Scan the bundled vulnerable target and open the dashboard:

    make scan SCAN_TARGET_DIR=examples/vulnerable-app
    make dev

    The dev server uses port 5174 by default and falls back to the next free port (e.g. 5175) when it is taken — check the Vite output for the actual URL.

  2. Explore the Results view (summary + findings table), open a finding for details, and read the Scan Guide.

  3. Build a trend: re-scan a few times (fix something, or point at another project). Every run is appended to the History tab, so you can watch the severity counts move over time.

  4. Try DAST safely against a target you own (baseline is passive):

    DAST_TARGET_URL=http://host.docker.internal:3000 make scan-dast
  5. Run the quality gates:

    make lint && make test   # fast, no Docker
    make integration         # real scanners (needs Docker)
    make e2e                 # browser tests

The app

  • Scan — enter a local folder (SAST/SCA/IaC/secret/supply-chain), a URL (DAST), or a container image, and run a scan from the UI; you can also drag & drop an existing scan-report.json here to inspect it. This needs a native runner, so scanning is active in the desktop app; in the browser it shows guidance and the upload box instead.
  • Results — one scan on a page: a visual summary (totals, severity donut, findings-by-type bar chart, per-scanner status) followed by a sortable, filterable table with a detail drawer (file/line, CVE/CWE, package, remediation). Clicking a summary card or scanner row narrows the table below. The browser opens here; the desktop app reaches it by drilling in from a scan or a history run.
  • History — trend of findings by severity across past runs, plus a table of every scan (click to reopen it). The CLI appends each make scan to a local, file-based history store (history/index.json + one file per run); the desktop app records the scans it runs in localStorage. No database, and the index shape mirrors a future hosted scan_runs table.
  • Scan Guide — explains every category (SAST, DAST, SCA, IaC, secret, container image, supply-chain, supply-risk) and generates copy-paste CLI commands for each (the DAST command updates as you type a target URL).
  • Settings (desktop) — store a Socket.dev API key so the supply-risk scan adds behavioral analysis; it's kept locally and passed to the scan.

Unified report format

A report looks like this:

{
  "schemaVersion": "1",
  "generatedAt": "2026-06-30T09:15:00.000Z",
  "target": "demo-app",
  "scanners": [
    {
      "scanner": "semgrep",
      "type": "sast",
      "status": "ok", // ok | skipped | error
      "durationMs": 8421,
      "findings": [
        {
          "id": "semgrep:...:src/db/query.ts:42",
          "scanner": "semgrep",
          "type": "sast",
          "ruleId": "...tainted-sql-string",
          "title": "tainted-sql-string",
          "severity": "high", // critical | high | medium | low | info | unknown
          "message": "...",
          "file": "src/db/query.ts",
          "startLine": 42,
          "cwe": ["CWE-89: SQL Injection"],
          "remediation": "Use parameterized queries.",
        },
      ],
    },
  ],
  "summary": {
    "total": 13,
    "bySeverity": { "...": 0 },
    "byType": {},
    "byScanner": {},
  },
}

CI

Two GitHub Actions workflows ship with the repo:

  • .github/workflows/ci.yml — quality gate (lint → type-check → test → build) on every push and PR.
  • .github/workflows/security-scan.yml — reference pipeline running the same scanners (Semgrep, Trivy, Checkov, OSV-Scanner, and ZAP on demand). It uses workflow_dispatch (manual trigger) by default so it never consumes Actions minutes on its own — switch it to push / pull_request to gate merges in a real project.

Where it runs

The scanners run as Docker containers, so the CLI needs a host with a Docker daemon:

  • Local (default). Run make scan on your machine — your source and the findings never leave it. The outbound traffic is pulling scanner images and their vulnerability databases, plus dependency lookups against public vulnerability APIs: OSV-Scanner queries OSV.dev with package names/versions by default (an offline DB mode is available), and the Socket.dev supply-risk check is opt-in (off unless you set a key). Neither sends your source code.
  • A VM with Docker (e.g. a small cloud VM / Fly.io machine). The CLI works unchanged because it can still launch sibling containers.

Scope

This is a focused demo. In scope: the CLI, the unified report, the dashboard, and the reference CI workflow. Out of scope: running scans from the web UI, authentication, SIEM/alerting integrations, and production monitoring.

License

MIT

About

DevSec Scanner — local-first security scanner: SAST, SCA, IaC, DAST, secret, container-image & supply-chain scans from one CLI or desktop app, unified into one report.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages