From 52e2b74bcd581c070f8982d498eed61197e0927e Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:25:05 +0100 Subject: [PATCH] ci(dogfood): make the invisible-character gate fail closed PR #90 fixed the pattern so it can match at all, but the step still never failed: findings were emitted as ::warning and grep errors were sent to /dev/null (Codacy MEDIUM on #90, valid). A gate that only warns is not a gate. - planted positive at the top of the step: if the pattern does not fire on a literal NBSP the step exits 1 (the class of fault #90 repaired) - per-file annotations are ::error and the step exits 1 when any file matches; the summary step runs with if: always() - no 2>/dev/null on the thing under test - LICENSING.adoc, PROJECT_SUMMARY.adoc, SECURITY.adoc: five U+00A0 replaced with spaces so the first real run is green on merit Verified locally: step exits 0 on this tree, exits 1 with a planted NBSP file. First red must be recorded in standards gate-proofs. --- .github/workflows/dogfood-gate.yml | 35 ++++++++++++++++++++++-------- LICENSING.adoc | 2 +- PROJECT_SUMMARY.adoc | 6 ++--- SECURITY.adoc | 2 +- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index da5723f..0599c0f 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -128,9 +128,22 @@ jobs: run: | # Inline invisible character detection (from empty-linter's core patterns). # Checks for: zero-width spaces, zero-width joiners, BOM, soft hyphens, - # non-breaking spaces, null bytes, and other invisible Unicode in source files. - set +e + # non-breaking spaces, null bytes, C0 controls and other invisible + # Unicode in source files. + # + # `(*UTF)` makes PCRE match code points, not bytes, whatever the + # runner locale is; `-a` stops grep treating a NUL-bearing file as + # binary and silently matching nothing. Both were missing before + # #90 and the gate had matched nothing in its life. PATTERNS='(*UTF)[\x00-\x08\x0B\x0C\x0E-\x1F\x{a0}\x{ad}\x{200b}-\x{200f}\x{202a}-\x{202f}\x{2060}\x{2066}-\x{2069}\x{feff}]' + + # Planted positive: the pattern must fire on a known NBSP, or this + # gate cannot fire at all and must fail closed rather than pass. + if ! printf 'x\xc2\xa0y' | grep -aqP "$PATTERNS"; then + echo "::error::invisible-character pattern did not match a planted NBSP; the gate cannot fire, failing closed" + exit 1 + fi + find "$GITHUB_WORKSPACE" \ -not -path '*/.git/*' -not -path '*/node_modules/*' \ -not -path '*/.deno/*' -not -path '*/target/*' \ @@ -141,23 +154,27 @@ jobs: -o -name '*.yml' -o -name '*.yaml' -o -name '*.md' -o -name '*.adoc' \ -o -name '*.idr' -o -name '*.zig' -o -name '*.v' -o -name '*.jl' \ -o -name '*.gleam' -o -name '*.hs' -o -name '*.ml' -o -name '*.sh' \) \ - -exec grep -aPrl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt 2>/dev/null - EL_EXIT=$? - set -e + -exec grep -aPrl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt - FINDINGS=$(wc -l < /tmp/empty-lint-results.txt 2>/dev/null || echo 0) + FINDINGS=$(grep -c . /tmp/empty-lint-results.txt || true) echo "findings=$FINDINGS" >> "$GITHUB_OUTPUT" - echo "exit_code=$EL_EXIT" >> "$GITHUB_OUTPUT" echo "ready=true" >> "$GITHUB_OUTPUT" - # Emit annotations for each file with invisible chars + # One annotation per offending file, then FAIL: a gate that only + # warns is not a gate. while IFS= read -r filepath; do [ -z "$filepath" ] && continue REL_PATH="${filepath#$GITHUB_WORKSPACE/}" - echo "::warning file=${REL_PATH}::Invisible Unicode characters detected (zero-width space, BOM, NBSP, etc.)" + echo "::error file=${REL_PATH}::Invisible Unicode characters detected (zero-width space, BOM, NBSP, soft hyphen, C0 control, NUL)" done < /tmp/empty-lint-results.txt + if [ "$FINDINGS" -gt 0 ]; then + echo "::error::${FINDINGS} file(s) contain invisible Unicode characters (listed above). This gate fails the job." + exit 1 + fi + - name: Write summary + if: always() run: | if [ "${{ steps.lint.outputs.ready }}" = "true" ]; then FINDINGS="${{ steps.lint.outputs.findings }}" diff --git a/LICENSING.adoc b/LICENSING.adoc index 6896254..df43fad 100644 --- a/LICENSING.adoc +++ b/LICENSING.adoc @@ -52,7 +52,7 @@ distribution 3. Provide attribution as specified in your chosen license === Clarifications -*Q: Can I mix and match terms from both licenses?* A: No. Choose one +*Q: Can I mix and match terms from both licenses?* A: No. Choose one license and follow its terms entirely. *Q: What if I’m unsure which to use?* A: MIT is the simpler, more widely diff --git a/PROJECT_SUMMARY.adoc b/PROJECT_SUMMARY.adoc index edea269..b0451f0 100644 --- a/PROJECT_SUMMARY.adoc +++ b/PROJECT_SUMMARY.adoc @@ -160,7 +160,7 @@ timing patterns ==== Scheduling -✅ Occupation-based activity schedules ✅ Weekday vs. weekend patterns +✅ Occupation-based activity schedules ✅ Weekday vs. weekend patterns ✅ Hour-based active periods ✅ Activity intensity modulation ✅ Schedule-aware simulation @@ -179,7 +179,7 @@ calculation ✅ Data export functionality ==== Privacy & Security ✅ All data stored locally ✅ No external API calls ✅ Separate storage -for real vs. simulated data ✅ Configurable privacy modes ✅ Activity +for real vs. simulated data ✅ Configurable privacy modes ✅ Activity can be disabled instantly ==== Analytics Dashboard @@ -309,7 +309,7 @@ for future development . *Complete WASM Integration* * Load actual compiled module * Test performance -* Benchmark vs. mock implementation +* Benchmark vs. mock implementation . *Design Assets* * Create professional icons * Add branding diff --git a/SECURITY.adoc b/SECURITY.adoc index 2a46cae..2575585 100644 --- a/SECURITY.adoc +++ b/SECURITY.adoc @@ -20,7 +20,7 @@ security considerations: simulated) - Storage injection attacks (malicious data in chrome.storage) - WASM sandbox escapes - Content script injection vulnerabilities - Extension permission abuse - Timing side-channels -revealing real vs. simulated activity +revealing real vs. simulated activity *Out of Scope*: - Physical access attacks - Browser zero-days (rely on browser vendor patches) - Social engineering of users - Cryptographic