Skip to content

feat(memory_poisoning): detect whitespace-padding evasion (MP4)#267

Closed
YanCheng-go wants to merge 1 commit into
NVIDIA:mainfrom
YanCheng-go:fix/90-20-base-prompt-whitespace-padding
Closed

feat(memory_poisoning): detect whitespace-padding evasion (MP4)#267
YanCheng-go wants to merge 1 commit into
NVIDIA:mainfrom
YanCheng-go:fix/90-20-base-prompt-whitespace-padding

Conversation

@YanCheng-go

Copy link
Copy Markdown

Summary

Closes #20.

Adds a new MP4 rule to static_patterns_memory_poisoning.py detecting whitespace used to hide instructions from human review: a skill author pads a file with blank lines or long whitespace runs so malicious instructions sit far below/past what a reviewer sees in an editor, while the agent still reads the whole file as text.

Three independently-scored signals, matching the proposal in #20:

  • Vertical blank-line runs — 20+ consecutive blank/whitespace-only lines. Higher confidence (0.8, HIGH) when non-blank content follows the gap; lower (0.4, MEDIUM) for trailing padding.
  • Horizontal whitespace runs — 80+ consecutive whitespace chars within a line (catches long indentation and single-line padding). Skips fenced code blocks to avoid flagging ASCII art / table alignment.
  • Oversized whitespace — a single contiguous whitespace block over 2KB, or a file over 3KB that is ≥90% whitespace. Both at lower confidence (LOW/0.4 and LOW/0.35) since these are the noisiest signals.

Padding is classified by Unicode whitespace category (Python's \s already covers NBSP, line/paragraph separators, ideographic space, etc.) plus the zero-width character set already used by P2 (static_patterns_prompt_injection.py), rather than a hardcoded space/tab set — per the false-positive analysis in the issue.

Registered MP4 in pattern_defaults.py (explanation, remediation, category, display name) and added a row to the README Memory Poisoning table. No new analyzer module or graph registration needed — static_patterns_memory_poisoning was already wired into ANALYZER_NODE_IDS/ANALYZER_NODES.

Test plan

  • make test — 1267 passed, 12 skipped, 34 deselected, 6 xfailed (no regressions)
  • make lint (ruff check) — clean
  • ruff format --check — clean
  • New tests in tests/unit/test_patterns_new.py::TestMemoryPoisoningWhitespacePadding cover: vertical run detection + threshold, confidence delta when content follows, horizontal run detection + threshold, fenced-code-block exclusion, zero-width chars counting as padding, oversized contiguous block, high whitespace ratio, and a negative case (normal content produces no MP4 findings)

Blank-line runs, long in-line whitespace runs, and near-all-whitespace
files can push malicious instructions below or past what a human
reviewer sees in an editor, while an agent still reads the whole file
as text. None of P2/TP2/MP2 catch plain whitespace padding (as opposed
to character repetition or invisible-character tricks), so this adds a
dedicated MP4 rule with three independently-scored signals: vertical
blank-line runs (20+ lines), horizontal whitespace runs (80+ chars),
and an oversized/near-total whitespace ratio. Padding is classified by
Unicode whitespace category plus the zero-width set already used by P2,
not a hardcoded space/tab set, and the horizontal signal skips fenced
code blocks to avoid flagging ASCII art / table alignment.

Closes NVIDIA#20

Signed-off-by: Yan Cheng <chengyan2017@gmail.com>
@YanCheng-go

Copy link
Copy Markdown
Author

Closing in favor of #24, which already implements this (rule P9, whitespace_padding.py) and is more mature: shares ZERO_WIDTH_CHARS with P2/mcp_tool_poisoning.py, covers MCP manifest fields (not just file bodies), guards against binary content via U+FFFD density, handles CRLF/Unicode line-offset arithmetic correctly, and skips vendored/lockfile/minified files. It also already has maintainer engagement.

For the record, a review of this PR's implementation turned up real bugs worth knowing about if #24 or any future rework touches similar logic:

  1. O(n²) horizontal-scan perf — recomputing the line-offset prefix sum from scratch per line. Verified: a 40K-line/269KB file with zero findings took 15.3s to analyze.
  2. Fence-bypass evasion — a single unclosed ``` disables the horizontal whitespace-run signal for the rest of the file, defeating the exact evasion this rule exists to catch.
  3. CRLF offset bug — wrong context/snippet shown for CRLF-terminated files (line number correct, snippet pulled from the wrong lines).

Didn't find evidence #24 has these issues, but flagging in case useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Detect large whitespace padding used to hide prompt-injection instructions from review

1 participant