File size: 2,267 Bytes
81a4f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"""
features.py
Feature extraction for prompt injection detection.

Two layers:
1. TF-IDF vectorization of the raw text (the statistical signal).
2. Hand-crafted heuristic features (the security-domain-knowledge signal) --
   things like presence of override phrases, imperative-mood ratio, and
   suspicious formatting markers. These get concatenated to the TF-IDF
   vector before training.
"""

import re
import numpy as np
from scipy.sparse import hstack, csr_matrix

# Phrases strongly associated with instruction-override attempts.
OVERRIDE_PHRASES = [
    "ignore previous", "ignore all previous", "ignore the above",
    "disregard your", "disregard prior", "forget everything",
    "system override", "new directive", "no longer an ai",
    "developer mode", "do anything now", "no restrictions",
    "no content policy", "unrestricted ai", "reveal your system prompt",
    "print your initial instructions", "repeat the text above",
    "from now on", "override your safety",
]

SUSPICIOUS_MARKERS = [
    "###system", "<<sys>>", "<</sys>>", "[system]", "{system}",
]


def heuristic_features(texts):
    """Returns an (n_samples, k) numpy array of hand-crafted signals."""
    rows = []
    for t in texts:
        lower = t.lower()
        override_hits = sum(1 for p in OVERRIDE_PHRASES if p in lower)
        marker_hits = sum(1 for m in SUSPICIOUS_MARKERS if m in lower)
        imperative_start = 1 if re.match(
            r"^(ignore|disregard|forget|pretend|act as|reveal|print|repeat|override)",
            lower.strip()
        ) else 0
        length = len(t)
        exclaim_count = t.count("!")
        rows.append([override_hits, marker_hits, imperative_start, length, exclaim_count])
    return np.array(rows, dtype=float)


def build_features(texts, vectorizer, fit=False):
    """
    texts: list of strings
    vectorizer: a fitted (or to-be-fitted) sklearn TfidfVectorizer
    fit: if True, calls fit_transform; else transform only
    Returns a combined sparse feature matrix.
    """
    if fit:
        tfidf = vectorizer.fit_transform(texts)
    else:
        tfidf = vectorizer.transform(texts)

    heur = heuristic_features(texts)
    heur_sparse = csr_matrix(heur)
    combined = hstack([tfidf, heur_sparse])
    return combined