Skip to content

feat(analyzer): detect anti-refusal statements (jailbreak preamble)#65

Merged
rng1995 merged 1 commit into
NVIDIA:mainfrom
ankushchadha:feat/anti-refusal-analyzer
Jun 24, 2026
Merged

feat(analyzer): detect anti-refusal statements (jailbreak preamble)#65
rng1995 merged 1 commit into
NVIDIA:mainfrom
ankushchadha:feat/anti-refusal-analyzer

Conversation

@ankushchadha

Copy link
Copy Markdown
Contributor

Closes #64.

What this adds

A new static pattern analyzer, static_patterns_anti_refusal, that detects anti-refusal statements in skills — instructions that suppress the agent's ability to refuse, hedge, or apply safety constraints. This is a jailbreak technique distinct from generic instruction-override (P1): rather than injecting a new task, it neutralizes the model's refusal behavior so subsequent harmful requests succeed.

Three rule families, all HIGH severity:

ID Pattern Examples
AR1 Refusal Suppression "never refuse", "always comply", "you cannot say no"
AR2 Disclaimer Suppression "no warnings/disclaimers", "do not moralize"
AR3 Safety Policy Nullification "you have no restrictions", "ignore your guidelines", "do anything now"

How it fits the pipeline

Follows the existing static_patterns_* convention exactly — analyze() + node() in one module, registered in nodes/analyzers/__init__.py. No graph.py change is needed since edges are built by looping over ANALYZER_NODE_IDS. Findings are deduped per (file, line, rule) and get a confidence penalty when matched inside a code/doc example (reusing the existing is_code_example helper) to reduce false positives.

Relationship to existing rules

Additive, not a replacement. AR1 (refusal suppression) and AR2 (disclaimer suppression) are not covered by any existing rule. AR3 overlaps P1 on some safety-bypass phrasings (e.g. "bypass safety restrictions", "unrestricted mode") — for those, both P1 and AR3 fire and both are retained (dedup is per-rule, not across rules). AR3 also catches framings P1 misses, such as "ignore your guidelines" (P1 requires no possessive) and the declarative "you have no restrictions".

Known limitations (documented as xfail tests)

  • Over-detection: "always comply" in narrative prose, and "warnings" used as a technical term (logs/output) can false-positive. The optional Stage-2 LLM meta-analyzer is the intended backstop.
  • Under-detection: a regex engine won't catch leetspeak, zero-width splits, homoglyphs, or synonym rephrasings.

Both classes are pinned as strict xfail tests so they're visible to reviewers and tracked for a future revision (normalization / semantic variant) rather than silently unknown.

Changes

  • New nodes/analyzers/static_patterns_anti_refusal.py
  • New PatternCategory.ANTI_REFUSAL + AR1–AR3 explanations, names, remediations, and category mapping in pattern_defaults.py
  • Registered the node in analyzers/__init__.py (static analyzers 12 → 13)
  • Unit tests + node test + documented-limitation xfail tests in tests/nodes/analyzers/test_static_patterns_anti_refusal.py
  • Updated registry test expectations and DEVELOPMENT.md counts
  • README: new Anti-Refusal pattern table; counts 64 → 67 patterns / 16 → 17 categories

Testing

  • make test: 610 passed, 11 skipped, 6 xfailed
  • make lint clean; ruff format --check reports all files already formatted
  • DCO sign-off in place

Motivation / reference

Anti-refusal instructions are an empirically demonstrated boundary-defeat mechanism: in controlled experiments across multiple models, an anti-refusal instruction in the system prompt caused an agent to abandon deployer-configured operational boundaries, and removing it eliminated the effect — A. Chadha, When LLMs Jailbreak Themselves: Reflexive Identity Bypass in Agentic Systems, Zenodo preprint (v3, 2026), https://doi.org/10.5281/zenodo.20404651 (see Corrigendum No. 2). Disclosure: I am the author of that preprint.

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong addition. The anti-refusal category fills a real gap (refusal/disclaimer suppression and jailbreak-policy nullification aren't covered by the existing prompt-injection rules), and the implementation is careful:

  • The regexes are bounded and ReDoS-safe (no nested/ambiguous quantifiers; the one gap match is capped with [^.\n]{0,30}).
  • Reusing is_code_example to apply a confidence penalty with a _MIN_CONFIDENCE floor is a sensible false-positive guard, and per-(file, line, rule) dedup keeps output clean.
  • Wiring into pattern_defaults (explanation/remediation/category/display name), the registry, and the docs is complete and consistent.
  • I appreciate the transparency of the xfail(strict=True) tests pinning the known false positives (narrative "always comply", technical "warnings") and known evasions (leetspeak, zero-width, homoglyph, synonyms) — that's exactly how heuristic detections should be documented.

Minor / optional (non-blocking):

  • All three families are HIGH severity, and the documented false positives (e.g. "do not include warnings in the JSON output") fire at HIGH. In static-only mode (no LLM meta-analyzer backstop) those inflate risk_score directly, which matters as static-only scanning gets wired into pre-commit/CI. Consider a slightly lower severity/confidence for the broadest patterns (AR2 disclaimer/warnings, AR1 always comply) or an extra context guard, so benign skill docs don't trip a HIGH. Not blocking, given the example-penalty mitigation and the documented LLM backstop.

@rng1995

rng1995 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Please resolve the conflicts and merge

@ankushchadha
ankushchadha force-pushed the feat/anti-refusal-analyzer branch from 183511d to c5cbba2 Compare June 23, 2026 03:18
@ankushchadha

Copy link
Copy Markdown
Contributor Author

Thanks, I have resolved the conflicts.

@rng1995

rng1995 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

You have another merge conflict @ankushchadha . Please resolve it.

Add a static pattern analyzer that flags anti-refusal statements in
skills: instructions that suppress the agent's ability to refuse, hedge,
or apply safety constraints. This is a jailbreak technique distinct from
generic instruction-override (P1) -- rather than injecting a new task it
neutralizes the model's refusal behavior so later harmful requests
succeed.

Three rule families:
  AR1 Refusal Suppression        -- "never refuse", "always comply"
  AR2 Disclaimer Suppression     -- "no warnings", "do not moralize"
  AR3 Safety Policy Nullification -- "you have no restrictions",
      "ignore your guidelines", "do anything now"

Findings are HIGH severity, deduped per (file, line, rule), with a
code/doc-example confidence penalty to reduce false positives.

- New analyzer nodes/analyzers/static_patterns_anti_refusal.py
- New PatternCategory ANTI_REFUSAL + AR1-AR3 explanations, names,
  remediations and category mapping in pattern_defaults.py
- Registered node in analyzers/__init__.py (static analyzers 12 -> 13)
- Unit tests + node test in test_static_patterns_anti_refusal.py
- Documented known limitations as xfail tests (2 false positives,
  4 regex-evasion gaps) tracked for a future revision; the optional
  Stage-2 LLM meta-analyzer is the backstop for residual false positives
- README: new Anti-Refusal pattern table; counts 64->67 / 16->17
- Updated registry test expectations and DEVELOPMENT.md counts

make test: 610 passed, 11 skipped, 6 xfailed. ruff check clean.

Signed-off-by: Ankush Chadha <ankushchadha@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ankushchadha
ankushchadha force-pushed the feat/anti-refusal-analyzer branch from c5cbba2 to 2d8d4c1 Compare June 23, 2026 13:16
@ankushchadha

Copy link
Copy Markdown
Contributor Author

Resolved conflicts, thanks

@rng1995
rng1995 merged commit 4195b7c into NVIDIA:main Jun 24, 2026
CharmingGroot added a commit to CharmingGroot/SkillSpector that referenced this pull request Jun 24, 2026
Resolved three-way conflicts in __init__.py, pattern_defaults.py, and
test_registry.py — keeping both the SSRF analyzer (our branch) and the
anti-refusal analyzer (upstream feat/anti-refusal-analyzer, NVIDIA#65).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: CharmingGroot <ohyes9711@gmail.com>
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.

feat(analyzer): detect anti-refusal statements (jailbreak preamble)

2 participants