@brunosps00/dev-workflow 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +2 -2
  2. package/bin/dev-workflow.js +5 -0
  3. package/lib/constants.js +2 -2
  4. package/lib/install-deps.js +88 -3
  5. package/lib/setup-wsl-browser.js +149 -0
  6. package/package.json +1 -1
  7. package/scaffold/en/commands/dw-analyze-project.md +22 -0
  8. package/scaffold/en/commands/dw-autopilot.md +3 -1
  9. package/scaffold/en/commands/dw-functional-doc.md +11 -3
  10. package/scaffold/en/commands/dw-generate-pr.md +2 -2
  11. package/scaffold/en/commands/dw-qa.md +1 -1
  12. package/scaffold/en/commands/dw-redesign-ui.md +2 -2
  13. package/scaffold/en/commands/dw-review.md +7 -5
  14. package/scaffold/en/commands/dw-secure-audit.md +72 -15
  15. package/scaffold/en/templates/constitution-template.md +8 -3
  16. package/scaffold/en/templates/functional-doc/playwright.flow.mjs.tpl +17 -0
  17. package/scaffold/pt-br/commands/dw-analyze-project.md +22 -0
  18. package/scaffold/pt-br/commands/dw-autopilot.md +3 -1
  19. package/scaffold/pt-br/commands/dw-functional-doc.md +11 -3
  20. package/scaffold/pt-br/commands/dw-generate-pr.md +2 -2
  21. package/scaffold/pt-br/commands/dw-qa.md +1 -1
  22. package/scaffold/pt-br/commands/dw-redesign-ui.md +2 -2
  23. package/scaffold/pt-br/commands/dw-review.md +7 -5
  24. package/scaffold/pt-br/commands/dw-secure-audit.md +72 -15
  25. package/scaffold/pt-br/templates/constitution-template.md +8 -3
  26. package/scaffold/pt-br/templates/functional-doc/playwright.flow.mjs.tpl +17 -0
  27. package/scaffold/scripts/cdp-relay/Cargo.toml +16 -0
  28. package/scaffold/scripts/cdp-relay/src/main.rs +49 -0
  29. package/scaffold/scripts/functional-doc/generate-dossier.mjs +7 -9
  30. package/scaffold/scripts/functional-doc/run-playwright-flow.mjs +192 -183
  31. package/scaffold/scripts/lib/capture-screenshots.mjs +133 -0
  32. package/scaffold/scripts/lib/resolve-browser.mjs +405 -0
  33. package/scaffold/scripts/lib/ui-slop-detect.mjs +102 -0
  34. package/scaffold/skills/dw-testing-discipline/references/playwright-recipes.md +50 -0
  35. package/scaffold/skills/dw-ui-discipline/SKILL.md +5 -0
  36. package/scaffold/skills/dw-ui-discipline/references/color-oklch.md +60 -0
  37. package/scaffold/skills/dw-ui-discipline/references/motion.md +52 -0
  38. package/scaffold/skills/dw-ui-discipline/references/responsive.md +52 -0
  39. package/scaffold/skills/dw-ui-discipline/references/type-scale.md +48 -0
  40. package/scaffold/skills/dw-ui-discipline/references/ux-writing.md +52 -0
  41. package/scaffold/skills/security-review/SKILL.md +19 -0
  42. package/scaffold/skills/security-review/references/sast.md +67 -0
  43. package/scaffold/skills/security-review/references/secrets.md +46 -0
  44. package/scaffold/en/templates/functional-doc/playwright.spec.ts.tpl +0 -19
  45. package/scaffold/pt-br/templates/functional-doc/playwright.spec.ts.tpl +0 -19
@@ -92,6 +92,11 @@ Without this sentence, defaults take over: light mode, default contrasts, animat
92
92
  | Designing state coverage | `references/state-matrix.md` |
93
93
  | Interactive widget (button, form, modal, anything clickable) | `references/accessibility-floor.md` |
94
94
  | No design authority exists in the project | `references/curated-defaults.md` (palettes / fonts / scales) |
95
+ | Choosing or reviewing a palette / dark mode | `references/color-oklch.md` (OKLCH, tinted neutrals, contrast) |
96
+ | Fonts, sizing, hierarchy, line length | `references/type-scale.md` (modular scale, pairing, measure) |
97
+ | Adding animation or transitions | `references/motion.md` (easing, duration, reduced-motion) |
98
+ | Cross-device / breakpoints / touch | `references/responsive.md` (mobile-first, container queries) |
99
+ | Any user-facing copy (labels, errors, empty states) | `references/ux-writing.md` |
95
100
 
96
101
  ## The 14 visual-slop patterns (full catalog in `references/visual-slop.md`)
97
102
 
@@ -0,0 +1,60 @@
1
+ # Color & contrast — OKLCH, tinted neutrals, dark mode
2
+
3
+ > Patterns adapted from [impeccable](https://github.com/pbakaus/impeccable) (Apache-2.0, derived from
4
+ > Anthropic's frontend-design skill). Read when building or reviewing a palette.
5
+
6
+ ## Work in OKLCH, not hex/HSL
7
+
8
+ OKLCH (`oklch(L C H)`) is perceptually uniform: equal lightness steps *look* equally spaced, and changing
9
+ hue does not shift perceived brightness (HSL's big flaw — HSL yellow at 50% L looks far lighter than HSL
10
+ blue at 50% L). Modern browsers support `oklch()` natively.
11
+
12
+ - **L** (lightness) 0–100% — drive your scale off this.
13
+ - **C** (chroma) 0–~0.37 — saturation. Keep neutrals low (0.005–0.03), accents moderate (0.1–0.2).
14
+ - **H** (hue) 0–360.
15
+
16
+ ## Tinted neutrals (the single highest-leverage move)
17
+
18
+ Pure grays (`#777`, `oklch(50% 0 0)`) look dead. Tint every neutral slightly toward one hue — usually the
19
+ brand hue or its complement — so the UI feels intentional.
20
+
21
+ ```css
22
+ /* Cool, slightly blue-tinted neutral scale */
23
+ --n-50: oklch(98% 0.005 250);
24
+ --n-100: oklch(96% 0.006 250);
25
+ --n-300: oklch(87% 0.010 250);
26
+ --n-500: oklch(62% 0.014 250);
27
+ --n-700: oklch(42% 0.014 250);
28
+ --n-900: oklch(22% 0.012 250);
29
+ --n-950: oklch(14% 0.010 250);
30
+ ```
31
+
32
+ Pure `#000` / `#fff` are anti-patterns (the detector flags `pure-black-white`). Use `oklch(14% 0.01 H)`
33
+ for "black" surfaces and `oklch(98% 0.005 H)` for "white".
34
+
35
+ ## Building a scale
36
+
37
+ Pick the hue + chroma, then step lightness on a roughly even curve (98 / 96 / 92 / 87 / 78 / 68 / 62 / 52 /
38
+ 42 / 32 / 22 / 14). Keep chroma roughly constant for neutrals; for an accent, let chroma peak in the mid
39
+ lightness range (most vivid at 55–65% L) and fall off at the extremes.
40
+
41
+ ## Accent & semantic colors
42
+
43
+ - One primary accent carries the brand. Avoid the training-data default blue `#3B82F6` — shift the hue or
44
+ darken it (`oklch(55% 0.18 255)` reads more deliberate).
45
+ - Semantic set: success (green ~145H), warning (amber ~75H), danger (red ~25H), info (blue ~250H). Keep
46
+ their lightness consistent with each other so badges feel like a family.
47
+
48
+ ## Contrast (non-negotiable, see accessibility-floor.md)
49
+
50
+ - Body text ≥ 4.5:1; large text (≥24px or ≥18.7px bold) and UI component boundaries ≥ 3:1.
51
+ - Don't rely on hue alone to convey state — pair color with icon/text (color-blind users).
52
+ - Test the *actual* foreground/background pair, not the token names.
53
+
54
+ ## Dark mode
55
+
56
+ Dark mode is not "invert lightness". Re-tune:
57
+ - Raise surface lightness in steps as elements come forward (`oklch(16%)` page → `18%` card → `22%`
58
+ popover) instead of borders everywhere.
59
+ - Reduce accent chroma slightly in dark mode — vivid colors vibrate on dark backgrounds.
60
+ - Keep body text around `oklch(90% 0.01 H)`, not pure white, to cut glare.
@@ -0,0 +1,52 @@
1
+ # Motion — easing, duration, stagger, reduced motion
2
+
3
+ > Patterns adapted from [impeccable](https://github.com/pbakaus/impeccable) (Apache-2.0, derived from
4
+ > Anthropic's frontend-design skill). Read when adding animation or transitions.
5
+
6
+ ## Motion conveys state, not decoration
7
+
8
+ Animate to explain a change: where did this come from, what just happened, where did it go. If a motion
9
+ doesn't answer one of those, cut it. Decorative motion is a visual-slop pattern.
10
+
11
+ ## Easing — avoid bounce/elastic
12
+
13
+ Real objects decelerate smoothly. Bounce and elastic easing feel dated and tacky (the detector flags
14
+ `bounce-easing` / spring overshoot like `cubic-bezier(0.68,-0.55,0.27,1.55)`).
15
+
16
+ - **Enter** (element appears): ease-out — fast start, soft landing. `cubic-bezier(0.16, 1, 0.3, 1)`
17
+ (ease-out-expo) or `ease-out`.
18
+ - **Exit** (element leaves): ease-in — accelerate away. `cubic-bezier(0.4, 0, 1, 1)`.
19
+ - **Move/resize on screen:** ease-in-out (standard) `cubic-bezier(0.4, 0, 0.2, 1)`.
20
+ - Never linear for UI (only for continuous things like spinners/marquees).
21
+
22
+ ## Duration
23
+
24
+ - Micro (hover, small fades): 100–150ms.
25
+ - Standard (dropdowns, toggles, cards): 200–300ms.
26
+ - Large (page/route, full-screen sheets): 300–500ms.
27
+ - Over ~500ms feels sluggish; under ~80ms is imperceptible (just snap with no transition).
28
+
29
+ ## Stagger
30
+
31
+ When several items enter together, stagger them 20–50ms apart so the eye follows the sequence — but cap
32
+ total stagger (don't make the 12th list item wait 600ms). Stagger entrances, not exits.
33
+
34
+ ## What to animate (performance)
35
+
36
+ Animate only `transform` and `opacity` — they run on the compositor and don't trigger layout/paint.
37
+ Animating `width`, `height`, `top`, `left`, `margin` janks. Use `transform: translate/scale` instead.
38
+
39
+ ## `prefers-reduced-motion` (required)
40
+
41
+ Honor it. Replace movement with an instant or opacity-only change.
42
+
43
+ ```css
44
+ @media (prefers-reduced-motion: reduce) {
45
+ *, *::before, *::after {
46
+ animation-duration: 0.01ms !important;
47
+ transition-duration: 0.01ms !important;
48
+ }
49
+ }
50
+ ```
51
+
52
+ Don't remove *feedback* entirely — a state change still needs to be visible, just not kinetic.
@@ -0,0 +1,52 @@
1
+ # Responsive — mobile-first, container queries, fluid space
2
+
3
+ > Patterns adapted from [impeccable](https://github.com/pbakaus/impeccable) (Apache-2.0, derived from
4
+ > Anthropic's frontend-design skill). Read when a surface must work across devices.
5
+
6
+ ## Mobile-first
7
+
8
+ Author the small-screen layout first, then add complexity at larger widths with `min-width` queries.
9
+ Mobile-first CSS is additive (you enhance up), which produces simpler, smaller rules than desktop-first
10
+ (`max-width`) overrides. `/dw-redesign-ui` requires every proposal to describe mobile AND desktop.
11
+
12
+ ## Breakpoints
13
+
14
+ Base decisions on content, not device names. Reasonable defaults:
15
+ `640` (sm) · `768` (md) · `1024` (lg) · `1280` (xl). Add a breakpoint when the layout *breaks*, not at a
16
+ fixed phone width.
17
+
18
+ ## Container queries > viewport queries for components
19
+
20
+ A card that appears in a sidebar and in a main column should respond to *its container*, not the viewport.
21
+ Reusable components are far more robust with container queries:
22
+
23
+ ```css
24
+ .card-wrap { container-type: inline-size; }
25
+ @container (min-width: 28rem) {
26
+ .card { grid-template-columns: auto 1fr; }
27
+ }
28
+ ```
29
+
30
+ ## Fluid space and type
31
+
32
+ Use `clamp()` to interpolate spacing/type between breakpoints instead of hard jumps:
33
+
34
+ ```css
35
+ --gap: clamp(1rem, 0.5rem + 2vw, 2.5rem);
36
+ h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
37
+ ```
38
+
39
+ Keep body text fixed (≥16px); fluid body harms readability.
40
+
41
+ ## Touch & input
42
+
43
+ - Touch targets ≥ 24×24 CSS px (≥ 44×44 recommended on mobile). Spacing between targets matters as much
44
+ as size.
45
+ - Don't hide primary actions behind hover — hover doesn't exist on touch. Provide a tap-visible path.
46
+ - Respect safe-area insets on mobile (`env(safe-area-inset-*)`) for notches/home indicators.
47
+
48
+ ## Layout reflow
49
+
50
+ State explicitly how elements reorganize between breakpoints: stack vs. side-by-side, what collapses into
51
+ a menu, what hides (and whether hidden content is still reachable). "Hidden on mobile with no alternative"
52
+ is a bug, not a layout decision.
@@ -0,0 +1,48 @@
1
+ # Typography — modular scale, pairing, measure
2
+
3
+ > Patterns adapted from [impeccable](https://github.com/pbakaus/impeccable) (Apache-2.0, derived from
4
+ > Anthropic's frontend-design skill). Read when choosing fonts or fixing hierarchy.
5
+
6
+ ## Modular scale
7
+
8
+ Pick a base size (16px body) and a ratio, then derive every step from it — don't hand-pick arbitrary
9
+ sizes. Common ratios: 1.2 (minor third, dense UI), 1.25 (major third, balanced), 1.333 (perfect fourth,
10
+ editorial/marketing).
11
+
12
+ Example at base 16 / ratio 1.25:
13
+ `12 · 14 · 16 · 20 · 25 · 31 · 39 · 49` (px). Map to roles: caption 12–14, body 16, h4 20, h3 25, h2 31,
14
+ h1 39–49. **Soft hierarchy** (headings barely larger than body) is a visual-slop pattern — make the jump
15
+ visible.
16
+
17
+ ## Fluid type
18
+
19
+ For headings that should scale with viewport, use `clamp()` instead of breakpoint steps:
20
+
21
+ ```css
22
+ h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
23
+ ```
24
+
25
+ Keep body text fixed (≥16px) — fluid body harms readability and zoom.
26
+
27
+ ## Pairing
28
+
29
+ - One typeface, multiple weights, is the safest path — pairs fail more often than they succeed.
30
+ - If pairing: contrast roles clearly (e.g., a distinctive display face for headings + a neutral grotesque
31
+ for body). Don't pair two similar sans fonts.
32
+ - Avoid the overused defaults that signal "no thought": Inter-everywhere, system-ui only, Montserrat
33
+ headings, Lobster/Pacifico for "personality". A deliberate grotesque or a good serif body reads better.
34
+
35
+ ## Weight, line-height, measure
36
+
37
+ - **Weight:** body 400–450; don't set body below 400 (thin fonts fail in bright light — see the scene
38
+ question). Headings 600–700. Avoid 300 for anything users must read.
39
+ - **Line-height:** body ~1.5–1.6; headings tighter ~1.1–1.25; the larger the text, the tighter.
40
+ - **Measure (line length):** 45–75 characters for body. Cap with `max-width: 65ch`. Over ~80ch the eye
41
+ loses the next line — the detector flags long lines.
42
+
43
+ ## Letter-spacing & numerics
44
+
45
+ - Tighten large headings slightly (`letter-spacing: -0.01em` to `-0.02em`); leave body alone.
46
+ - All-caps labels need positive tracking (`0.05em`).
47
+ - Use tabular figures (`font-variant-numeric: tabular-nums`) for tables, timers, prices so digits don't
48
+ jitter.
@@ -0,0 +1,52 @@
1
+ # UX writing — labels, errors, empty states
2
+
3
+ > Patterns adapted from [impeccable](https://github.com/pbakaus/impeccable) (Apache-2.0, derived from
4
+ > Anthropic's frontend-design skill). Read when writing any user-facing copy in UI.
5
+
6
+ ## Principles
7
+
8
+ - **Specific over generic.** Copy should be self-explanatory without a manual.
9
+ - **Verb-led for actions.** Tell the user what happens.
10
+ - **User's language, not the system's.** "Couldn't save your changes", not "Error 500".
11
+ - **Front-load.** Put the meaningful word first ("Delete project", not "Are you sure you want to…").
12
+
13
+ ## Button & link labels
14
+
15
+ Verb-less CTAs ("Get Started", "Learn More", "Submit", "OK", "Click Here") are a visual-slop pattern —
16
+ they make the user guess the outcome. Name the action and, when useful, the object:
17
+
18
+ - ✅ "Create invoice" · "Send reset link" · "Delete 3 files" · "Save and continue"
19
+ - ❌ "Submit" · "OK" · "Continue" (alone) · "Get Started"
20
+
21
+ Destructive actions say what they destroy and are visually distinct (not the same color as primary).
22
+
23
+ ## Error messages
24
+
25
+ A good error answers: what happened, why, and what to do next.
26
+
27
+ - ❌ "Invalid input." / "Something went wrong."
28
+ - ✅ "That email is already registered. Try signing in instead."
29
+ - ✅ "Card declined by your bank. Use a different card or contact your bank."
30
+
31
+ Attach errors to the field that caused them, announce them to screen readers (see accessibility-floor.md),
32
+ and never blame the user ("you entered it wrong" → "Enter a date in the future").
33
+
34
+ ## Empty states
35
+
36
+ A silent "No items found." centered on a blank page is a visual-slop pattern. An empty state should:
37
+ 1. Explain why it's empty (new vs. filtered-out vs. error).
38
+ 2. Give the next action (a primary button to create the first item, or "Clear filters").
39
+ 3. Optionally set expectations ("Invoices you create will appear here.").
40
+
41
+ Distinguish **first-run empty** (onboarding tone, call to create) from **no-results empty** (offer to
42
+ adjust the search/filter) from **error empty** (explain + retry).
43
+
44
+ ## Microcopy details
45
+
46
+ - Loading: say what's loading ("Loading invoices…") rather than a bare spinner for long ops.
47
+ - Success: confirm the outcome and, if relevant, where the result went — don't stack a toast for every
48
+ trivial event (toast-everywhere is slop).
49
+ - Numbers & dates: format for the locale; use relative time ("2 hours ago") where recency matters, exact
50
+ timestamps where precision matters.
51
+ - Sentence case for UI labels and buttons reads friendlier than Title Case in most product UIs; be
52
+ consistent either way.
@@ -37,6 +37,25 @@ Before flagging any issue, you MUST research the codebase to understand:
37
37
  | **MEDIUM** | Vulnerable pattern, input source unclear | **Note** as "Needs verification" |
38
38
  | **LOW** | Theoretical, best practice, defense-in-depth | **Do not report** |
39
39
 
40
+ ## Automated scanners + false-positive validation (fp-check)
41
+
42
+ The Security Gate (`/dw-secure-audit`) pairs this human-style review with deterministic tools:
43
+ - **SAST** — Semgrep, diff-focused on the generated code. See `references/sast.md`.
44
+ - **Secrets** — gitleaks (dedicated) + Trivy. See `references/secrets.md`. Any secret = REJECTED.
45
+
46
+ Tool findings are **not** auto-trusted to block. Apply the SAME reachability discipline to them before a
47
+ finding blocks the gate (the **fp-check** step):
48
+
49
+ 1. **Reachable?** Trace the flagged sink back — is it on a path attacker-controlled input can reach?
50
+ 2. **Controlled?** Is the input actually attacker-controlled, or a constant / server-controlled value
51
+ (see the table below) / framework-mitigated?
52
+ 3. **Verdict:** If both hold → keep as blocking at its tier. If provably unreachable or
53
+ trusted-input → **downgrade to advisory** and write the one-line justification in the findings file.
54
+ Never silently drop a tool finding — log every downgrade so the pattern stays auditable.
55
+
56
+ Exception: **secrets do not get an fp-check downgrade** — a real-looking credential is removed and
57
+ rotated, not argued away (only committed `.gitleaks.toml` allowlist entries for known fixtures apply).
58
+
40
59
  ## Do Not Flag
41
60
 
42
61
  ### General Rules
@@ -0,0 +1,67 @@
1
+ # SAST with Semgrep — semantic analysis of generated code
2
+
3
+ Static review by reading is necessary but misses patterns at scale. Semgrep adds deterministic,
4
+ semantic SAST. The Security Gate (`/dw-secure-audit`) runs it; this reference is the HOW.
5
+
6
+ > Tool: [Semgrep](https://semgrep.dev) (LGPL-2.1, OSS engine). Wrapped natively — no rule content is
7
+ > copied. Rulesets are pulled by name from the Semgrep registry (cached after first run).
8
+
9
+ ## Default invocation (diff-focused — the generated code)
10
+
11
+ The gate's primary job is the code just written, so scope Semgrep to the diff against the PR base:
12
+
13
+ ```bash
14
+ semgrep scan \
15
+ --config p/security-audit --config p/owasp-top-ten --config p/secrets \
16
+ --baseline-commit "$(git merge-base HEAD origin/main)" \
17
+ --json --output .dw/secure-audit/semgrep.json
18
+ ```
19
+
20
+ - `--baseline-commit` makes Semgrep report only findings **introduced** by the diff — no noise from
21
+ pre-existing code. Use the merge-base with the target branch (fall back to the branch point).
22
+ - Pinned rulesets keep runs reproducible: `p/security-audit`, `p/owasp-top-ten`, `p/secrets`. Add
23
+ language packs as detected (`p/javascript`, `p/python`, `p/csharp`, `p/rust`, `p/golang`).
24
+
25
+ ### Full-tree mode (`--full`)
26
+
27
+ For a periodic deep pass (not per-PR), drop `--baseline-commit` to scan the whole tree. Slower; use
28
+ when auditing an inherited codebase or on a schedule.
29
+
30
+ ## Severity mapping → gate tiers
31
+
32
+ Semgrep severities map to the Security Gate tiers (Rigoroso):
33
+
34
+ | Semgrep `severity` | Gate tier | Block? |
35
+ |--------------------|-----------|--------|
36
+ | `ERROR` | HIGH (or CRITICAL if CWE is RCE/authn-bypass/SQLi) | **YES** |
37
+ | `WARNING` | MEDIUM | Advisory |
38
+ | `INFO` | LOW | Advisory |
39
+
40
+ Read each finding's `extra.metadata` (`cwe`, `owasp`, `confidence`) to escalate ERROR→CRITICAL when the
41
+ CWE is in the high-impact set (CWE-89 SQLi, CWE-78 OS command, CWE-94 code injection, CWE-502
42
+ deserialization, CWE-287 authn, CWE-918 SSRF).
43
+
44
+ ## Parsing the JSON
45
+
46
+ Findings live under `results[]`: `check_id`, `path`, `start.line`, `extra.severity`,
47
+ `extra.message`, `extra.metadata`. Write a human report to `.dw/secure-audit/sast-findings.md`
48
+ grouped by tier, each finding citing `path:line` and the rule id. Errors under `errors[]` (e.g.,
49
+ ruleset fetch failure offline) → note "SAST partial" in the summary, do not crash.
50
+
51
+ ## False-positive validation (before blocking)
52
+
53
+ A blocking SAST finding must survive reachability validation (see the fp-check discipline in
54
+ `SKILL.md`): confirm the flagged sink is reached by attacker-controlled input. If the pattern is
55
+ provably unreachable or the input is fully trusted/constant, **downgrade to advisory** and record the
56
+ one-line justification in `sast-findings.md`. Never silently drop — always log the downgrade.
57
+
58
+ ## Tool absent
59
+
60
+ If `semgrep` is not installed, the gate skips this layer, records "SAST: skipped (semgrep not
61
+ installed)" in `audit-summary.md`, and points to `npx @brunosps00/dev-workflow install-deps`. The
62
+ gate does not fail merely because a scanner is missing — but the missing coverage is visible.
63
+
64
+ ## CodeQL (optional, advisory)
65
+
66
+ CodeQL is deeper but requires a build + database and is too slow for a per-PR gate. Treat it as an
67
+ opt-in periodic scan, not part of the default Security Gate path.
@@ -0,0 +1,46 @@
1
+ # Secret scanning with gitleaks
2
+
3
+ Hardcoded secrets are the highest-severity, lowest-tolerance finding: **any hit blocks the Security
4
+ Gate, with no ADR exception**. Trivy catches some; gitleaks is the dedicated, diff-aware scanner.
5
+
6
+ > Tool: [gitleaks](https://github.com/gitleaks/gitleaks) (MIT, OSS). Wrapped natively.
7
+
8
+ ## Diff-focused invocation (the generated code)
9
+
10
+ Scan what the diff introduced, so a pre-existing (already-rotated/allowlisted) secret doesn't block a
11
+ new PR, while any newly added secret does:
12
+
13
+ ```bash
14
+ # Uncommitted/staged changes (pre-commit style):
15
+ gitleaks protect --staged --redact --report-format json --report-path .dw/secure-audit/gitleaks.json
16
+
17
+ # Commits on this branch vs base (PR style):
18
+ gitleaks detect --redact --log-opts "$(git merge-base HEAD origin/main)..HEAD" \
19
+ --report-format json --report-path .dw/secure-audit/gitleaks.json
20
+ ```
21
+
22
+ `--redact` keeps the actual secret value out of the report/logs. A full-tree scan
23
+ (`gitleaks detect` with no `--log-opts`) is the periodic deep pass.
24
+
25
+ ## Verdict
26
+
27
+ - **≥1 finding → REJECTED.** No exception, no ADR. A leaked credential is rotated, not justified.
28
+ - Write `.dw/secure-audit/secret-findings.md`: rule id, `file:line`, commit, and the redacted match.
29
+ The remediation is always: remove from code + history, rotate the credential, move to a secret store.
30
+
31
+ ## Allowlisting (test fixtures / examples only)
32
+
33
+ Legitimate non-secrets (obvious placeholders, test fixtures) are allowlisted in a committed
34
+ `.gitleaks.toml` (regexes/paths), reviewed like code — not by skipping the scan. A real-looking
35
+ secret in a fixture should still be a fake (e.g., `AKIAIOSFODNN7EXAMPLE`), never a live one.
36
+
37
+ ## Relationship to Trivy
38
+
39
+ Trivy's secret scan stays (defense in depth, covers files gitleaks' git-history mode may miss).
40
+ gitleaks is authoritative for the diff. Dedupe by `file:line` in the report so one leak isn't
41
+ double-counted across tools.
42
+
43
+ ## Tool absent
44
+
45
+ If `gitleaks` is not installed, the gate falls back to Trivy secrets only and records "dedicated
46
+ secret scan: skipped (gitleaks not installed)" in the summary, pointing to `install-deps`.
@@ -1,19 +0,0 @@
1
- import { test, expect } from "@playwright/test";
2
-
3
- const BASE_URL = process.env.BASE_URL ?? "{{baseUrl}}";
4
-
5
- test("{{testTitle}}", async ({ page }) => {
6
- const evidence = [] as string[];
7
-
8
- await test.step("Open target route", async () => {
9
- await page.goto(`${BASE_URL}{{routePath}}`);
10
- await expect(page).toHaveURL(new RegExp("{{routeRegex}}"));
11
- evidence.push("navigation");
12
- });
13
-
14
- {{testSteps}}
15
-
16
- await test.step("Record final context", async () => {
17
- expect(evidence.length).toBeGreaterThan(0);
18
- });
19
- });
@@ -1,19 +0,0 @@
1
- import { test, expect } from "@playwright/test";
2
-
3
- const BASE_URL = process.env.BASE_URL ?? "{{baseUrl}}";
4
-
5
- test("{{testTitle}}", async ({ page }) => {
6
- const evidence = [] as string[];
7
-
8
- await test.step("Abrir rota alvo", async () => {
9
- await page.goto(`${BASE_URL}{{routePath}}`);
10
- await expect(page).toHaveURL(new RegExp("{{routeRegex}}"));
11
- evidence.push("navigation");
12
- });
13
-
14
- {{testSteps}}
15
-
16
- await test.step("Registrar contexto final", async () => {
17
- expect(evidence.length).toBeGreaterThan(0);
18
- });
19
- });