- make quick/check/mutation from cj/security-baseline (Makefile unchanged, settings in baseline.mk), pre-commit, gitleaks, Semgrep rules, ESLint security rules, Forgejo workflow - tests/security: generic baseline tests plus app config and role tests against createApp() with an in-memory database - extract createApp() from src/server/index.ts; LB_DATABASE_PATH for tests - fix gaps found by the tests: reject unknown fields on project create, answer malformed JSON with 400 instead of 500, set nosniff on the API - npm audit fix (next 16.4.1 and others, no high/critical left) - clear ESLint errors, safeFileName without control-character regex - per-project ownership stays an open, documented gap (idorGap -> TODO) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.9 KiB
YAML
76 lines
2.9 KiB
YAML
rules:
|
|
- id: react-dangerously-set-inner-html
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: dangerouslySetInnerHTML ist ein XSS-Einfallstor. Text rendern oder mit DOMPurify säubern.
|
|
pattern-either:
|
|
- pattern: <$EL dangerouslySetInnerHTML={...} />
|
|
- pattern: <$EL dangerouslySetInnerHTML={...}>...</$EL>
|
|
- pattern: "{dangerouslySetInnerHTML: ...}"
|
|
|
|
- id: dom-inner-html
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: innerHTML/outerHTML/insertAdjacentHTML mit dynamischem Inhalt (XSS). textContent verwenden.
|
|
patterns:
|
|
- pattern-either:
|
|
- pattern: $EL.innerHTML = $V
|
|
- pattern: $EL.outerHTML = $V
|
|
- pattern: $EL.insertAdjacentHTML($POS, $V)
|
|
- pattern: document.write($V)
|
|
- pattern-not: $EL.innerHTML = "..."
|
|
- pattern-not: $EL.innerHTML = ``
|
|
|
|
- id: token-in-web-storage
|
|
languages: [typescript, javascript]
|
|
severity: WARNING
|
|
message: Token/Secret im Web Storage ist per XSS auslesbar. HttpOnly-Cookie verwenden.
|
|
patterns:
|
|
- pattern-either:
|
|
- pattern: localStorage.setItem($K, ...)
|
|
- pattern: sessionStorage.setItem($K, ...)
|
|
- pattern: window.localStorage.setItem($K, ...)
|
|
- metavariable-regex:
|
|
metavariable: $K
|
|
regex: (?i).*(token|jwt|secret|auth|session|password|api.?key).*
|
|
|
|
- id: express-cors-wildcard
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: CORS erlaubt jede Origin (bei credentials besonders gefährlich). Origins explizit auflisten.
|
|
pattern-either:
|
|
- pattern: cors()
|
|
- pattern: 'cors({..., origin: "*", ...})'
|
|
- pattern: 'cors({..., origin: true, ...})'
|
|
- pattern: $RES.setHeader("Access-Control-Allow-Origin", "*")
|
|
- pattern: $RES.header("Access-Control-Allow-Origin", "*")
|
|
|
|
- id: sql-template-string
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: SQL per Template-String oder Verkettung. Platzhalter/Query-Builder verwenden.
|
|
patterns:
|
|
- pattern-either:
|
|
- pattern: $DB.$M(`...${$X}...`, ...)
|
|
- pattern: $DB.$M("..." + $X, ...)
|
|
- pattern: sql.raw(`...${$X}...`)
|
|
- pattern: sql.raw("..." + $X)
|
|
- metavariable-regex:
|
|
metavariable: $M
|
|
regex: ^(query|exec|execute|prepare|run|all|get|raw|\$queryRawUnsafe|\$executeRawUnsafe)$
|
|
|
|
- id: tls-verification-disabled-node
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: TLS-Zertifikatsprüfung abgeschaltet.
|
|
pattern-either:
|
|
- pattern: "{..., rejectUnauthorized: false, ...}"
|
|
- pattern: process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
|
|
|
|
- id: jwt-decode-without-verify
|
|
languages: [typescript, javascript]
|
|
severity: ERROR
|
|
message: jwt.decode prüft keine Signatur. jwt.verify verwenden.
|
|
pattern-either:
|
|
- pattern: jwt.decode(...)
|
|
- pattern: 'jwt.verify($T, $K, {..., algorithms: [..., "none", ...], ...})'
|