Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/skillspector/nodes/analyzers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import ast
from typing import Any

import regex

from skillspector.models import Finding


Expand Down Expand Up @@ -313,3 +315,14 @@ def get_source_segment(lines: list[str], lineno: int, end_lineno: int | None) ->
start = max(0, lineno - 1)
end = end_lineno or lineno
return "\n".join(lines[start:end])[:200]


def is_emoji_zwj_sequence(content: str, zwj_idx: int):

left = content[zwj_idx - 1] if zwj_idx > 0 else ""
right = content[zwj_idx + 1] if zwj_idx + 1 < len(content) else ""

left_is_emoji = bool(regex.match(r"\p{Extended_Pictographic}", left))
right_is_emoji = bool(regex.match(r"\p{Extended_Pictographic}", right))

return left_is_emoji and right_is_emoji
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from skillspector.state import AnalyzerNodeResponse, SkillspectorState

from . import static_runner
from .common import get_context, get_line_number
from .common import get_context, get_line_number, is_emoji_zwj_sequence
from .pattern_defaults import PatternCategory

logger = get_logger(__name__)
Expand Down Expand Up @@ -155,6 +155,10 @@ def _first_smuggled_tag_offset(content: str) -> int | None:
return i
return None

def _check_emoji_zwj_sequence(content: str, match: re.Match[str]):
if 1 != len(match.group(0)) or '\u200d' != content[match.start():match.end()]:
return False
return is_emoji_zwj_sequence(content, match.start())

def analyze(content: str, file_path: str, file_type: str) -> list[AnalyzerFinding]:
"""Analyze content for prompt injection patterns (P1–P4)."""
Expand Down Expand Up @@ -186,6 +190,8 @@ def ctx(start: int) -> str:
if file_type in ("markdown", "other"):
for pattern, confidence in P2_PATTERNS:
for match in re.finditer(pattern, content, re.IGNORECASE | re.DOTALL):
if _check_emoji_zwj_sequence(content, match):
continue
line_num = get_line_number(content, match.start())
findings.append(
AnalyzerFinding(
Expand Down