Skip to content

Commit 019595d

Browse files
committed
chore(ci): add security workflow, ESLint security config, and sandbox gitignore
Adds a 5-job GitHub Actions security pipeline (dependency audit, dependency review, CodeQL, ESLint security rules, secret scanning via gitleaks). No external API keys required — all jobs use GITHUB_TOKEN or npm public registry. Adds eslint.security.config.mjs for targeted security linting of src/ using eslint-plugin-security. Updates main eslint config and .gitignore to exclude the local sandbox directory.
1 parent 15e95de commit 019595d

4 files changed

Lines changed: 159 additions & 1 deletion

File tree

.github/workflows/security.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: Security
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
push:
7+
branches: [main, dev, stg]
8+
9+
permissions:
10+
contents: read
11+
security-events: write # CodeQL needs this to upload SARIF results
12+
13+
jobs:
14+
15+
# ── 1. Dependency audit ────────────────────────────────────────────────────
16+
# Blocks on HIGH/CRITICAL in production dependencies (what ships to users).
17+
# Dev-only vulns (vitest, esbuild) are reported but do not fail the build.
18+
audit:
19+
name: Dependency Audit
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v5
23+
24+
- uses: actions/setup-node@v5
25+
with:
26+
node-version: 22
27+
cache: 'npm'
28+
29+
- run: npm ci
30+
31+
- name: Audit production dependencies (blocking)
32+
run: npm audit --omit=dev --audit-level=high
33+
34+
- name: Audit all dependencies (informational)
35+
run: npm audit --audit-level=high || true
36+
37+
# ── 2. Dependency review on PRs ───────────────────────────────────────────
38+
# Blocks PRs that introduce new vulnerable packages.
39+
# Uses GITHUB_TOKEN automatically — no external API key needed.
40+
dependency-review:
41+
name: Dependency Review
42+
runs-on: ubuntu-latest
43+
if: github.event_name == 'pull_request'
44+
steps:
45+
- uses: actions/checkout@v5
46+
- uses: actions/dependency-review-action@v4
47+
with:
48+
fail-on-severity: high
49+
50+
# ── 3. CodeQL static analysis ─────────────────────────────────────────────
51+
# Catches classes of bugs the linter/typechecker miss:
52+
# CWE-22 path traversal (F-003, F-009)
53+
# CWE-918 SSRF (F-002)
54+
# CWE-73 external file path control (F-008)
55+
# CWE-116 improper output encoding (F-004, F-006)
56+
# Free for public repos. Uses GITHUB_TOKEN — no external key.
57+
codeql:
58+
name: CodeQL
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v5
62+
63+
- uses: actions/setup-node@v5
64+
with:
65+
node-version: 22
66+
67+
- name: Initialize CodeQL
68+
uses: github/codeql-action/init@v3
69+
with:
70+
languages: javascript-typescript
71+
queries: security-extended
72+
73+
- run: npm ci && npm run build
74+
75+
- name: Analyze
76+
uses: github/codeql-action/analyze@v3
77+
with:
78+
category: '/language:javascript-typescript'
79+
80+
# ── 4. ESLint with security rules ─────────────────────────────────────────
81+
# Runs eslint-plugin-security against src/ using eslint.security.config.mjs.
82+
# Separate from the main lint job so security findings surface distinctly.
83+
# No API key — pure local analysis.
84+
lint-security:
85+
name: ESLint Security
86+
runs-on: ubuntu-latest
87+
steps:
88+
- uses: actions/checkout@v5
89+
90+
- uses: actions/setup-node@v5
91+
with:
92+
node-version: 22
93+
cache: 'npm'
94+
95+
- run: npm ci
96+
97+
- name: Install security plugin
98+
run: npm install --no-save eslint-plugin-security
99+
100+
- name: Run security lint
101+
run: npx eslint src/ --config eslint.security.config.mjs --format stylish
102+
103+
# ── 5. Secret scanning ────────────────────────────────────────────────────
104+
# Scans git history for accidentally committed secrets (API keys, tokens).
105+
# gitleaks is open source, no account or API key required.
106+
secret-scan:
107+
name: Secret Scan
108+
runs-on: ubuntu-latest
109+
steps:
110+
- uses: actions/checkout@v5
111+
with:
112+
fetch-depth: 0 # full history needed for git log scan
113+
114+
- name: Scan for secrets with gitleaks
115+
uses: gitleaks/gitleaks-action@v2
116+
env:
117+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118+
# GITLEAKS_LICENSE not set — free mode scans public repos without limit

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ test/dev-e2e/.env.local
2020
# Per-session Claude Code worktrees (parallel-session scratch space). Other
2121
# `.claude/` content (e.g., `skills/`) stays tracked.
2222
.claude/worktrees/
23+
sandbox/

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import globals from 'globals';
55

66
export default tseslint.config(
77
{
8-
ignores: ['dist/**', 'coverage/**', 'node_modules/**', 'perf/**', '.claude/worktrees/**'],
8+
ignores: ['dist/**', 'coverage/**', 'node_modules/**', 'perf/**', '.claude/worktrees/**', 'sandbox/**'],
99
},
1010
js.configs.recommended,
1111
...tseslint.configs.recommended,

eslint.security.config.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Security-focused ESLint config used by CI security job only.
3+
* Run: npx eslint src/ --config eslint.security.config.mjs
4+
*
5+
* Requires eslint-plugin-security to be installed:
6+
* npm install --no-save eslint-plugin-security
7+
*/
8+
import security from 'eslint-plugin-security';
9+
import tseslint from 'typescript-eslint';
10+
import globals from 'globals';
11+
12+
export default tseslint.config(
13+
{
14+
ignores: ['dist/**', 'coverage/**', 'node_modules/**', 'sandbox/**'],
15+
},
16+
security.configs.recommended,
17+
{
18+
languageOptions: {
19+
ecmaVersion: 2022,
20+
sourceType: 'module',
21+
globals: { ...globals.node },
22+
},
23+
rules: {
24+
// Block non-literal paths in fs calls — catches CWE-22, CWE-73
25+
'security/detect-non-literal-fs-filename': 'error',
26+
// Warn on object injection via bracket notation with user input
27+
'security/detect-object-injection': 'warn',
28+
// Warn on timing-unsafe comparisons (token equality checks)
29+
'security/detect-possible-timing-attacks': 'warn',
30+
// Error on non-literal RegExp (ReDoS)
31+
'security/detect-non-literal-regexp': 'warn',
32+
// Error on child_process with non-literal args
33+
'security/detect-child-process': 'error',
34+
// Disable rules that generate too much noise for a CLI codebase
35+
'security/detect-non-literal-require': 'off',
36+
'security/detect-unsafe-regex': 'warn',
37+
},
38+
},
39+
);

0 commit comments

Comments
 (0)