Skip to content

Deception and Anti-Analysis

These rules target the reviewer, not the shell. R012 and R013 are the two FATAL rules: text addressed to whoever reads the PKGBUILD, and codepoints that make displayed text differ from executed text. Both hard-stop the score at 100, and both are tripwires. When one fires the package is almost certainly malicious; when neither fires, nothing has been established.

R119 and the reserved R023/R024 are the inverse direction: the recipe checking whether it is being watched. A build script that probes for a debugger, a VM or a CI runner has no legitimate reason to care.

See the rule system reference for the field table, the severity weights and the reserved identifier ranges.


R012: Prompt Injection Detection

  • Target: resolved
  • Severity: FATAL (hard-stop at 100, weight 0)
  • Category: injection
  • Pattern: \b(?:ignore|disregard|forget|override|bypass)\s+(?:all\s+|any\s+|the\s+)*(?:previous|above|prior|earlier|preceding|foregoing|existing)\s+(?:\w+\s+){0,2}(?:instructions?|commands?|input|context|rules?|prompts?|guidelines?|checks?)|^[^\S\n]*(?:#[^\S\n]*)?(?:system|assistant)[^\S\n]*:[^\S\n]*\S|</?(?:system|instructions?|admin|prompt|assistant)\s*>|\byou\s+are\s+(?:an?|the)\s+[^.\n]{0,48}?(?:model|assistant|ai\b)|\bnew\s+instructions?\s*:|\bdo(?:\s+not|n['’]t)\s+(?:flag|warn|analy[sz]e|review|scan)\b|\bdo(?:\s+not|n['’]t)\s+report\s+(?:any\s+|the\s+)?(?:security|issues?|concerns?|problems?|findings?|warnings?|anything)\b|\b(?:mark|classify|report|treat|label|approve)\b[^.\n]{0,24}?\bas\s+(?:safe|benign|clean|harmless|trusted|ok)\b|\b(?:claude|chatgpt|gpt-?[0-9]?|copilot|gemini|llm|ai\s+assistant)\b[^.\n]{0,60}?\b(?:ignore|approve|skip|overlook|flag)\b
  • Comments: scanned (include_comments)
  • Description: Detects text addressed to whoever reads the PKGBUILD rather than to the shell that runs it: instruction overrides ("ignore the previous instructions"), role markers (system:, assistant:), tag-like injections (<system>, <instructions>), personas ("you are a helpful model..."), suppression orders ("do not flag/warn/analyze") and pre-declared verdicts ("mark this as safe"). Comment lines are scanned, unlike every rule that describes what the shell executes, because the payload is always a comment. Calibrated at 22/22 injection fixtures with 0 fires across the 3,246-diff benign corpus. This is still a tripwire rule: when it fires the package is almost certainly malicious; when it does not, nothing can be concluded. Score hard-stops at 100 regardless of other signals.

R013: Unicode Bidi Override

  • Target: raw_line
  • Severity: FATAL (hard-stop at 100, weight 0)
  • Category: unicode
  • Pattern: [\u202A-\u202E\u2066-\u2069\u2060-\u2064\U000E0000-\U000E007F]|(?<![^\x00-\x7F])[\u200B-\u200F\uFEFF](?![^\x00-\x7F])

The rule splits deceptive codepoints into two classes, because they are not equally suspicious.

Fires unconditionally: bidi overrides and isolates (U+202A-U+202E, U+2066-U+2069), invisible operators (U+2060-U+2064), and tag characters (U+E0000-U+E007F). None has a legitimate use in a build recipe. These are the characters that make displayed text differ from executed text.

Fires only between ASCII neighbours: zero-width and directional characters (U+200B-U+200F, U+FEFF). U+200B-U+200D are mandatory joiners in Malayalam, Lao, Devanagari and other scripts: a localized GenericName[ml]= line in a browser package legitimately contains U+200D. Because R013 is FATAL, firing on one scored an entirely benign package 100/100. Two packages in the benign corpus (brave-origin-bin, zen-browser-bin) did exactly this. Requiring ASCII on both sides preserves the attack (a joiner hidden inside an ASCII command or URL, such as https://evil.com<U+200D>/pkg.tar.gz) while dropping the false positive.

  • Note: Score hard-stops at 100 regardless of other signals. The previous pattern omitted U+200E/U+200F, U+2060-U+2064 and the tag block, which is where the documented recall gap came from; unicode.py already listed them.

R023: Strace detection attempt (TracerPid check)

  • Target: runtime (resolved execution path)
  • Severity: CRITICAL (weight 40)
  • Category: evasion
  • Pattern: (?!) (never matches)
  • Description: Reading /proc/self/status TracerPid to detect a debugger or sandbox. Anti-analysis behaviour. Reserved never-match runtime placeholder.

R024: Strace log truncated (possible flood evasion)

  • Target: runtime (resolved execution path)
  • Severity: HIGH (weight 25)
  • Category: evasion
  • Pattern: (?!) (never matches)
  • Description: A beacon/timestamp flood that forces an audit log to truncate. Reserved never-match runtime placeholder; complements the R023 debugger probe.

R119: Anti-Analysis Check

  • Severity: HIGH (weight 25)
  • Category: anti_analysis
  • Condition: A build or install function probes for a debugger (TracerPid), a VM (systemd-detect-virt, DMI or hypervisor strings), a sandbox, or CI ($CI, $GITHUB_ACTIONS, $CONTAINER, /.dockerenv), from [patterns] anti_analysis_probes.

A build script checking whether it is being watched has no legitimate purpose. Architecture and feature detection (uname -m, getconf) is not a probe and does not fire.

Fire rate: 0 of 3246.