-
-
Notifications
You must be signed in to change notification settings - Fork 0
fix: the invisible-character gate detected nothing — codepoint escapes, not UTF-8 bytes #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,10 +113,16 @@ jobs: | |
| id: lint | ||
| 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. | ||
| # Checks for: C0 control characters, zero-width spaces/joiners, BOM, | ||
| # soft hyphens, NBSP, bidi overrides, word joiner, null bytes. | ||
| # | ||
| # ⚠ CODEPOINT escapes \x{a0}, NOT UTF-8 byte sequences \xc2\xa0. grep -P | ||
| # matches CHARACTERS, so the byte form never matched anything: the previous | ||
| # pattern caught 0 of 6 invisible-character test cases. Verified 2026-08-27. | ||
| # ⚠ -a is required or grep skips any file containing a NUL as "binary". | ||
| # ⚠ A LEADING BOM cannot be matched by grep (it strips one); checked separately. | ||
| set +e | ||
| PATTERNS='\xc2\xa0|\xe2\x80\x8b|\xe2\x80\x8c|\xe2\x80\x8d|\xef\xbb\xbf|\xc2\xad|\xe2\x80\x8e|\xe2\x80\x8f|\xe2\x80\xaa|\xe2\x80\xab|\xe2\x80\xac|\xe2\x80\xad|\xe2\x80\xae|\x00' | ||
| PATTERNS='\x00|[\x01-\x08\x0B\x0C\x0E-\x1F]|\x{a0}|\x{ad}|\x{200b}|\x{200c}|\x{200d}|\x{200e}|\x{200f}|\x{202a}|\x{202b}|\x{202c}|\x{202d}|\x{202e}|\x{2060}|\x{feff}' | ||
| find "$GITHUB_WORKSPACE" \ | ||
| -not -path '*/.git/*' -not -path '*/node_modules/*' \ | ||
| -not -path '*/.deno/*' -not -path '*/target/*' \ | ||
|
|
@@ -127,7 +133,16 @@ 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 -Prl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt 2>/dev/null | ||
| -exec grep -aPrl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt 2>/dev/null | ||
| # A LEADING BOM is stripped by grep before matching, so it must be | ||
| # checked byte-wise. Mid-file BOMs are caught by the pattern above. | ||
| while IFS= read -r bf; do | ||
| [ -z "$bf" ] && continue | ||
| if [ "$(head -c3 "$bf" | od -An -tx1 | tr -d " ")" = "efbbbf" ]; then | ||
| echo "$bf" >> /tmp/empty-lint-results.txt | ||
| fi | ||
| done < <(find "$GITHUB_WORKSPACE" -not -path '*/.git/*' -not -path '*/node_modules/*' -type f \( -name '*.yml' -o -name '*.yaml' -o -name '*.json' -o -name '*.toml' \) 2>/dev/null) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Reuse the primary scan's candidate set for BOM detection. The primary scan checks the extensions listed at Lines 131-135 and excludes paths such as As a result, leading BOMs in files such as 🤖 Prompt for AI Agents |
||
| sort -u -o /tmp/empty-lint-results.txt /tmp/empty-lint-results.txt 2>/dev/null || true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Capture the scan status before sorting results.
🤖 Prompt for AI Agents |
||
| EL_EXIT=$? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MEDIUM RISK The |
||
| set -e | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,13 +41,23 @@ pub fn known_artifacts() -> [ArtifactDef] { | |
| ] | ||
| } | ||
|
|
||
| // C0 control characters (U+0001..U+001F) are illegal in YAML and corrupt | ||
| // symbolic parsers. TAB (9), LF (10) and CR (13) are legitimate whitespace | ||
| // and are excluded. NULL (0) keeps its own named entry above. | ||
| pub fn is_c0_control(byte_val: Int) -> Bool { | ||
| byte_val > 0 && byte_val < 32 && byte_val != 9 && byte_val != 10 && byte_val != 13 | ||
| } | ||
|
|
||
| pub fn get_artifact_def(byte_val: Int) -> Option<ArtifactDef> { | ||
| let defs = known_artifacts(); | ||
| for d in defs { | ||
| if d.byte_value == byte_val { | ||
| return Some(d); | ||
| } | ||
| } | ||
| if is_c0_control(byte_val) { | ||
| return Some(#{ name: "C0-CONTROL", byte_value: byte_val, severity: Critical, fix_action: "remove" }); | ||
| } | ||
|
Comment on lines
+58
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Make
Apply 🤖 Prompt for AI Agents |
||
| None | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 MEDIUM RISK
The manual BOM detection logic is restricted to configuration files (.yml, .yaml, .json, .toml), while the general invisible-character scan covers all source code. This creates a gap where leading BOMs in critical file types like .sh (where it breaks shebangs) or .rs will go undetected. Expand the find pattern to match the extensions used in the main scanner. Additionally, consider using
find -print0andread -d ''to safely handle file paths containing spaces.