Package Exports
- @tinydarkforge/secgate
- @tinydarkforge/secgate/secgate.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@tinydarkforge/secgate) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
╔═══════╗ █████ █████ █████ █████ █████ █████ █████
║ ╔═══╗ ║ █ █ █ █ █ █ █ █
║ ║ ⊙ ║ ║ █████ ████ █ █ ███ █████ █ ████
║ ╚═══╝ ║ █ █ █ █ █ █ █ █ █
╠═══════╣ █████ █████ █████ █████ █ █ █ █████
║ ░░░░░ ║
╔═══╬═══════╬═══╗ ━━━━━━━━━━━━━ SECURITY GATE ━━━━━━━━━━━━━
║ ║ [===] ║ ║ Semgrep · Gitleaks · osv-scanner · Trivy
╚═══╬═══════╬═══╝ · npm audit — one command, one report,
║ ║ ║ ║ one exit code. MIT · No account · No tel.
╚═╝ ╚═╝SecGate is a tiny security gate for CI/CD. Runs Semgrep, Gitleaks, osv-scanner, Trivy, and npm audit in one command, normalizes findings into one report, fails the pipeline on CRITICAL or HIGH. No account. No telemetry. Local files only.
Status: Early release (
v0.2.4). Published with npm provenance. Report vulnerabilities via SECURITY.md.
░▒▓█ TL;DR
npx @tinydarkforge/secgate .Runs all five scanners against the current directory, writes a JSON report, a self-contained HTML report, and (optionally) SARIF. Exit 0 on clean, 1 on CRITICAL/HIGH findings. That's the whole product.
░▒▓█ What it does today
SecGate wraps five existing open-source scanners, runs them against a directory, and produces:
- One normalized JSON report across every scanner
- One self-contained HTML report with per-scanner tabs, dark mode, zero external assets
- One SARIF 2.1.0 file ready for GitHub Code Scanning upload
- One exit code —
1when CRITICAL or HIGH findings are present; blocks CI
SecGate does not ship its own analysis engine. Every finding originates from one of the five underlying tools. The value is orchestration, normalization, and a single exit code.
░▒▓█ Scanners
| Scanner | Category | Notes |
|---|---|---|
| Semgrep | SAST (static code) | OSS ruleset. 10+ languages. Extend via customSemgrepRules. |
| Gitleaks | Secrets & credentials | Working tree + git history (when .git/ present). Secrets redacted. |
| npm audit | Node dependencies (SCA) | Runs when package.json present. GitHub advisory DB. |
| osv-scanner | Polyglot SCA | npm, PyPI, Go, Cargo, Maven, RubyGems, Packagist, NuGet, Pub. |
| Trivy | IaC + License + Image | Terraform, Kubernetes, Dockerfile, CloudFormation. Base-image CVEs. |
Missing scanner binaries are skipped gracefully and noted in the report. No scanner is required; SecGate uses whatever is on $PATH.
░▒▓█ Positioning
SecGate is not a SOC platform, a compliance tool, or a vulnerability management system. It is a CI gate that aggregates scanner output and fails the build when something critical is found.
| Alternative | When to pick it instead of SecGate |
|---|---|
| Trivy standalone | You only scan containers and don't need SAST or secrets. |
| Semgrep OSS | You only need SAST with custom rules. |
| Snyk | You need a managed vuln DB, triage UX, Jira sync — and budget. |
| Aikido | You want SaaS dashboards and are OK with a hosted account. |
SecGate's niche: zero-config orchestration, no account, no telemetry, local output only, MIT. If you need SaaS-grade triage or compliance workflow, buy Snyk or Aikido. Full matrix: docs/comparison.md.
░▒▓█ Product vision
SecGate will become the open-source input layer for a broader security workflow. The core CLI — scan orchestration, SARIF output, baselines, suppressions, HTML report — stays MIT-licensed and free, forever. Future paid extensions may add hosted dashboards, org-wide policy management, compliance evidence packs, and multi-repo aggregation for teams that need more than a local gate. Those do not exist today. The OSS boundary is defined in OPEN-CORE.md.
░▒▓█ Prerequisites
Node.js >=18. External scanners are optional — install only the ones you want to run.
# macOS
brew install semgrep gitleaks osv-scanner trivy
# Linux
pip install semgrep
# gitleaks: https://github.com/gitleaks/gitleaks#installing
# osv-scanner: https://github.com/google/osv-scanner#installation
# trivy: https://aquasecurity.github.io/trivy/latest/getting-started/installation/░▒▓█ Install
From npm (recommended)
npm install -g @tinydarkforge/secgateOne-shot via npx (no install)
npx @tinydarkforge/secgate .From source
git clone https://github.com/tinydarkforge/SecGate.git
cd SecGate
npm install
chmod +x secgate.js
sudo ln -sf "$(pwd)/secgate.js" /usr/local/bin/secgate░▒▓█ Usage
# Scan current directory (dry-run, default)
secgate .
# Scan with auto-remediation (npm audit fix only — see warning below)
secgate . --apply
# Scan with debug output
secgate . --debug
# Scan specific path
secgate /path/to/project
# Write reports to a custom directory (default: the target)
secgate /path/to/project --output-dir /tmp/reports
# Strip absolute paths from the report (auto-on when CI=true)
secgate /path/to/project --strip-paths
# Version / help
secgate --version
secgate --helpExit codes
| Code | Meaning |
|---|---|
0 |
PASS — no CRITICAL or HIGH findings |
1 |
FAIL — CRITICAL or HIGH findings present |
2 |
Invalid target or CLI error |
░▒▓█ Security — --apply in untrusted repos
⚠
--applyexecutes remediations (npm audit fix) inside the scanned repo. Treat this as code execution against the target. Only use it on code you trust.
Hardening already in place:
- Every npm invocation under
--applypasses--ignore-scripts— maliciouspreinstall/postinstallscripts in the target'spackage.json(or its dependencies) are not executed. --applyis gated: refuses to run unlessSECGATE_CONFIRM_APPLY=1is set (CI / non-interactive) or the user typesyat an interactive TTY prompt.- Every
--applyexecution is recorded in the report'sauditLogfield and mirrored to stderr with timestamp and target.
Operator guidance:
- Do not run
--applyagainst untrusted or newly cloned third-party repos. Run dry-run first, review, decide. - In CI, prefer dry-run (
secgate .) and rely on the exit code to gate. If you must--apply, do it inside an isolated, ephemeral runner withSECGATE_CONFIRM_APPLY=1. - Report files default to the target directory. Use
--output-dirto redirect; a warning is printed to stderr whencwd !== target. - In CI,
--strip-pathsis auto-enabled to prevent host paths leaking into uploaded artifacts.
░▒▓█ Configuration
Create .secgate.config.json in your scan target directory. All fields are optional.
{
"failOn": ["critical", "high"],
"scanners": {
"semgrep": true,
"gitleaks": true,
"npm": true,
"osv": true,
"trivy": false
},
"severityOverrides": [
{ "rule": "npm-audit.lodash", "severity": "LOW" },
{ "rule": "trivy-DS*", "severity": "MEDIUM" }
],
"ignore": ["CVE-2024-12345", "npm:some-old-package*"],
"baselineFile": ".secgate-baseline.json",
"customSemgrepRules": "./rules/"
}JSON Schema: docs/config.schema.json
Field reference
| Field | Type | Default | Description |
|---|---|---|---|
failOn |
string[] |
["critical","high"] |
Severity tiers that cause exit 1 |
scanners |
object |
all true |
Set any scanner to false to skip it |
severityOverrides |
array |
[] |
Override severity for matching signatures (glob * supported) |
ignore |
string[] |
[] |
Drop findings whose signature matches (glob * supported) |
baselineFile |
string |
.secgate-baseline.json |
Path to baseline file (relative to target) |
customSemgrepRules |
string|null |
null |
Extra --config=<path> passed to semgrep |
Precedence
CLI flag > .secgate.config.json > built-in defaults--baselineand--update-baselineare CLI-only (no config equivalent).- Missing config file: silent, defaults apply. Invalid JSON: error logged, defaults apply.
Baseline workflow
# 1. Accept current state as baseline
secgate . --update-baseline
# 2. On subsequent runs, fail only on net-new findings
secgate . --baselineCommit .secgate-baseline.json to your repo. Baselined findings appear in reports with a baseline marker and are excluded from the fail-gate.
Inline suppression
Add a comment on the flagged line or the line immediately above:
// secgate:ignore <rule-id>
db.query(userInput);
db.execute(sql); // secgate:ignore my.rule.id
/* secgate:ignore my.rule.id */
dangerousCall();Suppressed findings are excluded from counters. The report's suppressions section records per-rule counts for audit.
░▒▓█ CI / CD
GitHub Actions — minimal
# .github/workflows/secgate.yml
- name: Run SecGate
run: npx @tinydarkforge/secgate .
# exits 1 on CRITICAL or HIGH findings — blocks the pipelineNon-blocking (report only)
- name: Run SecGate
run: npx @tinydarkforge/secgate . || true
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: secgate-report
path: |
secgate-v7-report.json
*.htmlAs a composite GitHub Action
A composite action is published at .github/actions/secgate/ in this repo.
- name: SecGate Security Gate
id: secgate
uses: tinydarkforge/SecGate/.github/actions/secgate@main
with:
target: "."
apply: "false"
fail-on: "critical,high"
format: "json,html,sarif"
- name: Upload HTML + JSON
if: always()
uses: actions/upload-artifact@v4
with:
name: secgate-report
path: |
secgate-v7-report.json
*.html
- name: Upload SARIF to Code Scanning
if: always() && steps.secgate.outputs.sarif-path != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.secgate.outputs.sarif-path }}
category: secgateAction inputs
| Input | Default | Description |
|---|---|---|
target |
. |
Directory to scan |
apply |
false |
Execute fixable remediations |
fail-on |
critical,high |
Severity levels that fail the step |
format |
json,html |
Output formats — comma-separated: json, html, sarif |
Action outputs
| Output | Description |
|---|---|
report-path |
Path to secgate-v7-report.json |
sarif-path |
Path to <repo>.sarif.json (set only when format includes SARIF) |
Pin to a full commit SHA for production workflows:
uses: tinydarkforge/SecGate/.github/actions/secgate@<full-sha>See .github/workflows/example-secgate.yml for a complete reference workflow.
░▒▓█ SARIF output
SecGate emits SARIF 2.1.0 alongside JSON and HTML. SARIF is the standard format consumed by GitHub Code Scanning, GitLab SAST, and other platforms.
secgate . --format sarif
# writes: secgate-v7-report.json, <repo>.html, <repo>.sarif.jsonThe --format flag accepts a comma-separated list. sarif is additive — JSON and HTML are always written:
secgate . --format json,html,sarif # same as above
secgate . --format sarif # also writes JSON + HTMLSARIF structure
- One
runs[]entry per scanner:semgrep,gitleaks,npm,osv,trivy,trivyImage(6 total). - Each finding maps to a
resultwithruleId = signature,levelderived from severity, andlocations[].physicalLocationwhen file/line data is present. properties["security-severity"]carries a CVSS-style score for GitHub's sort order:CRITICAL = 9.5,HIGH = 7.5,MEDIUM = 5,LOW = 2,UNKNOWN = 0.
Upload to GitHub Code Scanning
- name: Run SecGate
id: secgate
run: npx @tinydarkforge/secgate . --format sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: <repo-name>.sarif.json
category: secgate░▒▓█ Report output
Each run writes:
secgate-v7-report.json— machine-readable, schema below.<repo-name>.html— self-contained HTML report with per-scanner tabs, dark mode, zero external assets. Filename derived from the target directory name.<repo-name>.sarif.json— SARIF 2.1.0 file (only when--format sarifis passed).
JSON schema
{
"version": "0.2.4",
"timestamp": "ISO 8601",
"target": "/absolute/path",
"mode": "dry-run | apply",
"status": "PASS | FAIL",
"summary": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0,
"unknown": 0
},
"tools": {
"semgrep": "ran | clean | skipped | error | pending",
"gitleaks": "ran | clean | skipped | error | pending",
"npm": "ran | clean | skipped | error | pending",
"osv": "ran | clean | skipped | error | pending",
"trivy": "ran | clean | skipped | error | pending",
"trivyImage": "ran | clean | skipped | error | pending"
},
"findings": [
{
"tool": "gitleaks | semgrep | npm | osv | trivy | trivyImage",
"type": "secret | code | dependency | iac | license",
"severity": "CRITICAL | HIGH | MEDIUM | LOW | UNKNOWN",
"signature": "rule or package ID",
"message": "description",
"file": "relative or absolute path, or null",
"line": 42,
"col": 5,
"endLine": 42,
"fixable": false,
"fixableBy": "auto | manual | null"
}
],
"intelligence": {
"riskScore": 0,
"attackSurface": ["secret", "dependency", "iac", "license", "code"],
"reasoning": [{ "issue": "...", "why": "..." }],
"recommendations": ["..."]
},
"remediation": {
"plan": [{
"issue": "...",
"patch": {
"action": "...",
"cmd": "display string or null",
"exec": { "binary": "npm", "args": ["audit", "fix", "--ignore-scripts"], "cwd": "..." }
}
}],
"stagedChanges": [],
"executed": [],
"blocked": [],
"confidence": 100
},
"auditLog": [
{
"timestamp": "ISO 8601",
"event": "apply_start | apply_confirmed | apply_exec | apply_ok | apply_fail",
"target": "target path or repo basename (if --strip-paths)"
}
]
}Severity tiers
Every finding is normalized to one of five tiers at the addFinding() ingress:
| Tier | Meaning |
|---|---|
| CRITICAL | Exploitable now — secrets, CVSS ≥ 9, hardcoded-credential SAST rules |
| HIGH | High-impact — CVSS 7.0–8.9, Semgrep ERROR, rated HIGH upstream |
| MEDIUM | Meaningful — CVSS 4.0–6.9, WARNING, MODERATE |
| LOW | Informational — CVSS < 4.0, INFO, NOTE |
| UNKNOWN | Upstream provided no severity or an unrecognized value — surfaced rather than miscounted |
Fixability
fixableBy: "auto"—patch()returns an executable command;--applywill run it (currently onlynpm audit fix).fixableBy: "manual"— a patch exists but requires human action (upgrade, rotate, refactor).fixable: truemirrorsfixableBy === "auto"for CI convenience.
Tool states
| State | Meaning |
|---|---|
ran |
Tool executed, findings present |
clean |
Tool executed, no findings |
skipped |
Tool not installed, or target not applicable (no package.json, no lockfile) |
error |
Tool produced output that could not be parsed — re-run with --debug |
pending |
Tool did not run (should not appear in final reports) |
░▒▓█ Risk scoring
Findings are scored with static weights applied at ingress:
| Severity | Weight |
|---|---|
| CRITICAL | 10 |
| HIGH | 6 |
| MEDIUM | 3 |
| LOW | 1 |
The riskScore in the report is the sum of these weights across all findings. This is a heuristic count, not CVSS, not EPSS, not exploit-probability modeling. Use it to compare runs of the same repo over time — not as an absolute posture score.
░▒▓█ Documentation
| Doc | What's in it |
|---|---|
OPEN-CORE.md |
OSS core boundary and paid extension roadmap |
docs/comparison.md |
Feature matrix vs Snyk / Trivy / Semgrep / Aikido |
docs/coverage.md |
Scanner-to-category matrix, explicit gaps |
docs/tuning.md |
Thresholds, baselines, suppression, CI vs local defaults |
docs/threat-model.md |
STRIDE analysis, trust boundaries, mitigations |
docs/adr/ |
Architecture decision records |
SECURITY.md |
Vulnerability reporting, SLA, coordinated disclosure, supply-chain trust |
CONTRIBUTING.md |
Dev setup, branch + commit conventions, PR checklist |
░▒▓█ Contributing
See CONTRIBUTING.md. Report vulnerabilities privately per SECURITY.md — do not open public issues for security reports.
░▒▓█ License
MIT — © TinyDarkForge
╔═══╗
║ ⊙ ║ "BLOCK. SCAN. GATE."
╚═══╝