max / goingson
1 file changed,
+43 insertions,
-1 deletion
| @@ -41,7 +41,9 @@ hits=$(grep -rnE 'var\(--[a-z-]+,\s*#' "$FRONTEND" --include='*.js' --include='* | |||
| 41 | 41 | report "no-var-fallback-hex" "var(--token, #fallback) — drop the fallback; it bypasses themes." "$hits" | |
| 42 | 42 | ||
| 43 | 43 | # 4. No window.confirm / bare confirm() — route through GoingsOn.ui.showConfirmDialog. | |
| 44 | - | hits=$(grep -rnE '\b(window\.)?confirm\(' "$SRC_JS" 2>/dev/null | grep -vE 'showConfirmDialog|confirmDelete|//\s*\*|\*\s' || true) | |
| 44 | + | # Skip js/tests/: test fixtures carry attack payloads and mocks that | |
| 45 | + | # legitimately contain `confirm(`/`alert(` as data, not as app calls. | |
| 46 | + | hits=$(grep -rnE '\b(window\.)?confirm\(' "$SRC_JS" 2>/dev/null | grep -v '/tests/' | grep -vE 'showConfirmDialog|confirmDelete|//\s*\*|\*\s' || true) | |
| 45 | 47 | report "no-window-confirm" "window.confirm() — use GoingsOn.ui.showConfirmDialog instead." "$hits" | |
| 46 | 48 | ||
| 47 | 49 | # 5. No inline style= that touches color / background / border / shadow / font / padding values. | |
| @@ -53,10 +55,50 @@ hits=$(grep -rnE 'empty-dashboard-list|kanban-empty|virtual-scroller-empty' "$FR | |||
| 53 | 55 | report "no-deprecated-empty-states" "Deprecated class — use .empty-state with --compact / --dashboard / --error." "$hits" | |
| 54 | 56 | ||
| 55 | 57 | # 7. No native browser dialogs. Charter rule from Phase 7 roll-up. | |
| 58 | + | # Skip js/tests/ (attack payloads / mocks reference these as data, not calls). | |
| 56 | 59 | hits=$(grep -rnE '\b(window\.)?(confirm|prompt|alert)\(' "$SRC_JS" 2>/dev/null \ | |
| 60 | + | | grep -v '/tests/' \ | |
| 57 | 61 | | grep -vE 'showConfirmDialog|showPromptDialog|confirmDelete|//\s|\*\s' || true) | |
| 58 | 62 | report "no-native-dialogs" "window.confirm/prompt/alert are banned — use GoingsOn.ui.show{Confirm,Prompt}Dialog or showToast." "$hits" | |
| 59 | 63 | ||
| 64 | + | # 8. Frontend JS test suite, including the CHRONIC-XSS escaping-enforcement gate | |
| 65 | + | # (js/tests/run.js). GO has no CI and running node inside `cargo build` is an | |
| 66 | + | # anti-pattern, so the gate lives here in the lint script that's run before a | |
| 67 | + | # commit — wiring it in makes the "build-failing gate" literal rather than a | |
| 68 | + | # test you have to remember to invoke. run.js exits non-zero on any failure. | |
| 69 | + | if command -v node >/dev/null 2>&1; then | |
| 70 | + | if ! js_out=$(node "$SRC_JS/tests/run.js" 2>&1); then | |
| 71 | + | echo | |
| 72 | + | echo "[frontend-js-tests] JS test suite failed (includes the CHRONIC-XSS escaping gate):" | |
| 73 | + | echo "$js_out" | tail -40 | |
| 74 | + | violations=$((violations + 1)) | |
| 75 | + | fi | |
| 76 | + | else | |
| 77 | + | echo "[frontend-js-tests] WARNING: node not found — skipping JS tests and the XSS gate" | |
| 78 | + | fi | |
| 79 | + | ||
| 80 | + | # 9. Inline event-handler ratchet (CSP `unsafe-inline` drawdown). | |
| 81 | + | # Dropping `script-src 'unsafe-inline'` from tauri.conf.json requires | |
| 82 | + | # converting every inline `on<event>="..."` handler to addEventListener — CSP | |
| 83 | + | # has no nonce for inline handlers. That's a phased migration; this gate stops | |
| 84 | + | # BACKSLIDING in the meantime: the occurrence count may only go DOWN. When you | |
| 85 | + | # remove handlers, lower INLINE_HANDLER_BUDGET to the new count (the script | |
| 86 | + | # prints the new number on progress). When it reaches 0 and the two inline | |
| 87 | + | # <script> blocks (index.html, compose.html) are externalized, drop | |
| 88 | + | # 'unsafe-inline' from the CSP. Tests and minified bundles are excluded. | |
| 89 | + | INLINE_HANDLER_BUDGET=391 | |
| 90 | + | inline_count=$(grep -rlE "\bon[a-z]+=[\"']" "$FRONTEND" --include='*.html' --include='*.js' 2>/dev/null \ | |
| 91 | + | | grep -v '/tests/' | grep -v '\.min\.' \ | |
| 92 | + | | tr '\n' '\0' | xargs -0 grep -oE "\bon[a-z]+=[\"']" 2>/dev/null | wc -l | tr -d ' ') | |
| 93 | + | if [ "$inline_count" -gt "$INLINE_HANDLER_BUDGET" ]; then | |
| 94 | + | report "inline-handler-ratchet" \ | |
| 95 | + | "Inline event handlers rose to $inline_count (budget $INLINE_HANDLER_BUDGET). Don't add inline on*= attributes; wire events with addEventListener (data-* + delegation)." \ | |
| 96 | + | "count $inline_count > budget $INLINE_HANDLER_BUDGET" | |
| 97 | + | elif [ "$inline_count" -lt "$INLINE_HANDLER_BUDGET" ]; then | |
| 98 | + | echo "[inline-handler-ratchet] progress: $inline_count inline handlers (budget $INLINE_HANDLER_BUDGET)." | |
| 99 | + | echo " Lower INLINE_HANDLER_BUDGET in scripts/lint-frontend.sh to $inline_count to lock in the gain." | |
| 100 | + | fi | |
| 101 | + | ||
| 60 | 102 | if [ $violations -eq 0 ]; then | |
| 61 | 103 | echo "frontend lint: clean" | |
| 62 | 104 | exit 0 |