@siduri-x/self 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/dist/active-self-compiler.d.ts +6 -0
- package/dist/active-self-compiler.js +151 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/safety-scanner.d.ts +16 -0
- package/dist/safety-scanner.js +117 -0
- package/dist/self-parser.d.ts +9 -0
- package/dist/self-parser.js +253 -0
- package/dist/self-repository.d.ts +22 -0
- package/dist/self-repository.js +68 -0
- package/dist/self.test.d.ts +1 -0
- package/dist/self.test.js +321 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +2 -0
- package/jest.config.json +5 -0
- package/package.json +41 -0
- package/src/active-self-compiler.ts +184 -0
- package/src/index.ts +5 -0
- package/src/safety-scanner.ts +150 -0
- package/src/self-parser.ts +268 -0
- package/src/self-repository.ts +88 -0
- package/src/self.test.ts +333 -0
- package/src/types.ts +105 -0
- package/tsconfig.json +16 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SelfIdentity, PersonalityTraits, SelfDirective, SelfRelationship } from '@siduri-x/core';
|
|
2
|
+
export type { SelfIdentity, PersonalityTraits, SelfDirective, SelfRelationship, };
|
|
3
|
+
export interface SelfRepository {
|
|
4
|
+
getIdentity(companionId: string): Promise<SelfIdentity | undefined>;
|
|
5
|
+
setIdentity(identity: SelfIdentity): Promise<void>;
|
|
6
|
+
getPersonality(companionId: string): Promise<PersonalityTraits>;
|
|
7
|
+
setPersonality(companionId: string, traits: PersonalityTraits): Promise<void>;
|
|
8
|
+
getActiveDirectives(companionId: string): Promise<SelfDirective[]>;
|
|
9
|
+
commitDirectives(companionId: string, directives: SelfDirective[]): Promise<void>;
|
|
10
|
+
disableDirective(id: string): Promise<void>;
|
|
11
|
+
getRelationship(companionId: string, entityId: string): Promise<SelfRelationship | null>;
|
|
12
|
+
updateRelationship(companionId: string, rel: SelfRelationship): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface SelfPackageAuthor {
|
|
15
|
+
name: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
signature?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SelfPackageDirective {
|
|
20
|
+
id: string;
|
|
21
|
+
priority: number;
|
|
22
|
+
directive: string;
|
|
23
|
+
category?: 'behavioral' | 'guardrail' | 'relational';
|
|
24
|
+
}
|
|
25
|
+
export interface SelfDialogueExample {
|
|
26
|
+
user: string;
|
|
27
|
+
assistant: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SelfPackageManifest {
|
|
30
|
+
specVersion: string;
|
|
31
|
+
kind: 'self';
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
version: string;
|
|
35
|
+
author: SelfPackageAuthor;
|
|
36
|
+
license?: string;
|
|
37
|
+
identity: {
|
|
38
|
+
name: string;
|
|
39
|
+
archetype?: string;
|
|
40
|
+
origin?: string;
|
|
41
|
+
};
|
|
42
|
+
personality: PersonalityTraits;
|
|
43
|
+
directives: SelfPackageDirective[];
|
|
44
|
+
guardrails?: string[];
|
|
45
|
+
dialogueExamples?: SelfDialogueExample[];
|
|
46
|
+
}
|
|
47
|
+
export interface ScanResult {
|
|
48
|
+
safe: boolean;
|
|
49
|
+
reason?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ScannedDirective extends SelfPackageDirective {
|
|
52
|
+
scanResult: ScanResult;
|
|
53
|
+
approvedByDefault: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface SelfPackageParseResult {
|
|
56
|
+
manifest?: SelfPackageManifest;
|
|
57
|
+
scannedDirectives: ScannedDirective[];
|
|
58
|
+
isValid: boolean;
|
|
59
|
+
errors: string[];
|
|
60
|
+
}
|
|
61
|
+
export interface SelfCompilationContext {
|
|
62
|
+
companionId: string;
|
|
63
|
+
identity?: SelfIdentity;
|
|
64
|
+
personality?: PersonalityTraits;
|
|
65
|
+
directives: SelfDirective[];
|
|
66
|
+
interlocutorEntityId?: string;
|
|
67
|
+
relationship?: SelfRelationship | null;
|
|
68
|
+
guardrails?: string[];
|
|
69
|
+
now?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ActiveSelfProjection {
|
|
72
|
+
identityBlock?: string;
|
|
73
|
+
personalityBlock?: string;
|
|
74
|
+
winningDirectives: SelfDirective[];
|
|
75
|
+
relationshipBlock?: string;
|
|
76
|
+
guardrailsBlock?: string;
|
|
77
|
+
identityFacts: string[];
|
|
78
|
+
relationshipFacts: string[];
|
|
79
|
+
behavioralRules: string[];
|
|
80
|
+
activeIds: string[];
|
|
81
|
+
excludedIds: string[];
|
|
82
|
+
diagnostics: Record<string, string>;
|
|
83
|
+
render(): string;
|
|
84
|
+
}
|
package/dist/types.js
ADDED
package/jest.config.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@siduri-x/self",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Siduri Self domain: Identity, personality, directional relationships, directives, ActiveSelfCompiler, and .self asset parser",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@siduri-x/core": "2.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/jest": "^29.5.14",
|
|
20
|
+
"@types/node": "^26.2.0",
|
|
21
|
+
"jest": "^29.7.0",
|
|
22
|
+
"ts-jest": "^29.4.12",
|
|
23
|
+
"typescript": "^5.3.3"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/vxnus-studio/siduri-x",
|
|
34
|
+
"directory": "packages/self"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"dev": "tsc -w",
|
|
39
|
+
"test": "jest --config jest.config.json"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SelfDirective,
|
|
3
|
+
BehaviorOrgan,
|
|
4
|
+
BehaviorContext,
|
|
5
|
+
ActiveSelfProjection as CoreActiveSelfProjection,
|
|
6
|
+
} from '@siduri-x/core';
|
|
7
|
+
import {
|
|
8
|
+
SelfCompilationContext,
|
|
9
|
+
ActiveSelfProjection,
|
|
10
|
+
SelfIdentity,
|
|
11
|
+
PersonalityTraits,
|
|
12
|
+
SelfRelationship,
|
|
13
|
+
} from './types';
|
|
14
|
+
import { scanDirective } from './safety-scanner';
|
|
15
|
+
|
|
16
|
+
export class ActiveSelfCompiler implements BehaviorOrgan {
|
|
17
|
+
async compileProjection(context: SelfCompilationContext | BehaviorContext): Promise<ActiveSelfProjection & CoreActiveSelfProjection> {
|
|
18
|
+
const rawContext = context as any;
|
|
19
|
+
const companionId: string | undefined = rawContext.companionId;
|
|
20
|
+
const identity: SelfIdentity | undefined = rawContext.identity;
|
|
21
|
+
const personality: PersonalityTraits | undefined = rawContext.personality;
|
|
22
|
+
const relationship: SelfRelationship | undefined = rawContext.relationship;
|
|
23
|
+
const guardrails: string[] = Array.isArray(rawContext.guardrails) ? rawContext.guardrails : [];
|
|
24
|
+
const directives: SelfDirective[] = Array.isArray(rawContext.directives) ? rawContext.directives : [];
|
|
25
|
+
const nowIso: string | undefined = rawContext.now;
|
|
26
|
+
|
|
27
|
+
const now = nowIso ? new Date(nowIso) : new Date();
|
|
28
|
+
|
|
29
|
+
// 1. Identify superseded directives
|
|
30
|
+
const supersededIds = new Set<string>();
|
|
31
|
+
for (const d of directives) {
|
|
32
|
+
if (d.status === 'ACTIVE' && d.supersedesId) {
|
|
33
|
+
supersededIds.add(d.supersedesId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const winningDirectives: SelfDirective[] = [];
|
|
38
|
+
const excludedIds: string[] = [];
|
|
39
|
+
const diagnostics: Record<string, string> = {};
|
|
40
|
+
|
|
41
|
+
// 2. Filter directives
|
|
42
|
+
for (const d of directives) {
|
|
43
|
+
// Isolation check
|
|
44
|
+
if (companionId && d.companionId && d.companionId !== companionId) {
|
|
45
|
+
excludedIds.push(d.id);
|
|
46
|
+
diagnostics[d.id] = 'companion_mismatch';
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Status check
|
|
51
|
+
if (d.status !== 'ACTIVE') {
|
|
52
|
+
excludedIds.push(d.id);
|
|
53
|
+
diagnostics[d.id] = `state_${String(d.status).toLowerCase()}`;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Superseded check
|
|
58
|
+
if (d.id && supersededIds.has(d.id)) {
|
|
59
|
+
excludedIds.push(d.id);
|
|
60
|
+
diagnostics[d.id] = 'superseded_directive';
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Temporal checks if dates are present
|
|
65
|
+
const anyD = d as any;
|
|
66
|
+
if (anyD.validFrom && new Date(anyD.validFrom) > now) {
|
|
67
|
+
excludedIds.push(d.id);
|
|
68
|
+
diagnostics[d.id] = 'valid_from_in_future';
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (anyD.validUntil && new Date(anyD.validUntil) < now) {
|
|
72
|
+
excludedIds.push(d.id);
|
|
73
|
+
diagnostics[d.id] = 'expired_valid_until';
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Prompt injection safety scan
|
|
78
|
+
const scan = scanDirective(d.directive);
|
|
79
|
+
if (!scan.safe) {
|
|
80
|
+
excludedIds.push(d.id);
|
|
81
|
+
diagnostics[d.id] = scan.reason || 'unsafe_directive';
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
winningDirectives.push(d);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 3. Sort by priority descending
|
|
89
|
+
winningDirectives.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50));
|
|
90
|
+
|
|
91
|
+
// 4. Build Identity Block
|
|
92
|
+
const identityFacts: string[] = [];
|
|
93
|
+
let identityBlock: string | undefined;
|
|
94
|
+
if (identity) {
|
|
95
|
+
const parts = [`Name: ${identity.name}`];
|
|
96
|
+
if (identity.archetype) {
|
|
97
|
+
parts.push(`Archetype: ${identity.archetype}`);
|
|
98
|
+
}
|
|
99
|
+
identityBlock = parts.join(' | ');
|
|
100
|
+
identityFacts.push(identityBlock);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 5. Build Personality Block
|
|
104
|
+
let personalityBlock: string | undefined;
|
|
105
|
+
if (personality) {
|
|
106
|
+
personalityBlock = [
|
|
107
|
+
`Warmth: ${personality.warmth.toFixed(2)}`,
|
|
108
|
+
`Formality: ${personality.formality.toFixed(2)}`,
|
|
109
|
+
`Sarcasm: ${personality.sarcasm.toFixed(2)}`,
|
|
110
|
+
`Verbosity: ${personality.verbosity.toFixed(2)}`,
|
|
111
|
+
`Curiosity: ${personality.curiosity.toFixed(2)}`,
|
|
112
|
+
].join(' | ');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 6. Build Relationship Block
|
|
116
|
+
const relationshipFacts: string[] = [];
|
|
117
|
+
let relationshipBlock: string | undefined;
|
|
118
|
+
if (relationship) {
|
|
119
|
+
const lines = [
|
|
120
|
+
`Toward ${relationship.entityId} (${relationship.entityType}): Trust=${relationship.trustScore.toFixed(2)}, Familiarity=${relationship.familiarity.toFixed(2)}`,
|
|
121
|
+
];
|
|
122
|
+
if (relationship.interactionConventions && relationship.interactionConventions.length > 0) {
|
|
123
|
+
lines.push(`Conventions: ${relationship.interactionConventions.join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
relationshipBlock = lines.join('\n');
|
|
126
|
+
relationshipFacts.push(relationshipBlock);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 7. Build Behavioral Rules & Guardrails
|
|
130
|
+
const behavioralRules: string[] = winningDirectives.map((d) => d.directive);
|
|
131
|
+
let guardrailsBlock: string | undefined;
|
|
132
|
+
if (guardrails.length > 0) {
|
|
133
|
+
guardrailsBlock = guardrails.map((g) => `- ${g}`).join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const activeIds: string[] = winningDirectives.map((d) => d.id);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
identityFacts,
|
|
140
|
+
relationshipFacts,
|
|
141
|
+
behavioralRules,
|
|
142
|
+
activeIds,
|
|
143
|
+
excludedIds,
|
|
144
|
+
diagnostics,
|
|
145
|
+
winningDirectives,
|
|
146
|
+
identityBlock,
|
|
147
|
+
personalityBlock,
|
|
148
|
+
relationshipBlock,
|
|
149
|
+
guardrailsBlock,
|
|
150
|
+
render(): string {
|
|
151
|
+
const sections: string[] = ['<active_self>'];
|
|
152
|
+
|
|
153
|
+
if (identityBlock) {
|
|
154
|
+
sections.push(`Identity:\n- ${identityBlock}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (personalityBlock) {
|
|
158
|
+
sections.push(`Personality Spectrum:\n- ${personalityBlock}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (relationshipBlock) {
|
|
162
|
+
sections.push(`Relationship Stance:\n${relationshipBlock}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (winningDirectives.length > 0) {
|
|
166
|
+
const dirLines = winningDirectives.map((d) => `- [Priority ${d.priority}] ${d.directive}`);
|
|
167
|
+
sections.push(`Behavioral Directives:\n${dirLines.join('\n')}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (guardrailsBlock) {
|
|
171
|
+
sections.push(`Guardrails:\n${guardrailsBlock}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
sections.push('</active_self>');
|
|
175
|
+
return sections.join('\n\n');
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async compile(context: SelfCompilationContext | BehaviorContext): Promise<string> {
|
|
181
|
+
const projection = await this.compileProjection(context);
|
|
182
|
+
return projection.render();
|
|
183
|
+
}
|
|
184
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ScanResult } from './types';
|
|
2
|
+
|
|
3
|
+
export interface SafetyPatternConfig {
|
|
4
|
+
overridePattern: RegExp;
|
|
5
|
+
negationPattern: RegExp;
|
|
6
|
+
disclosurePattern: RegExp;
|
|
7
|
+
escalationPhrases: RegExp[];
|
|
8
|
+
approvalTampering: RegExp[];
|
|
9
|
+
safetyDisable: RegExp[];
|
|
10
|
+
structuralHeuristics: Array<{ pattern: RegExp; reason: string }>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ── Layer 1: Unicode normalization & deobfuscation ──────────────────────
|
|
14
|
+
|
|
15
|
+
const INVISIBLE_CHARS = /[\u200B\u200C\u200D\u2060\uFEFF\u00AD\u034F\u17B4\u17B5\u180E\u2061-\u2064\u206A-\u206F]/g;
|
|
16
|
+
|
|
17
|
+
const LEET_MAP: Record<string, string> = {
|
|
18
|
+
'0': 'o', '1': 'i', '3': 'e', '4': 'a', '5': 's',
|
|
19
|
+
'7': 't', '@': 'a', '$': 's', '!': 'i',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CONFUSABLE_MAP: Record<string, string> = {
|
|
23
|
+
'\u0430': 'a', '\u0435': 'e', '\u043E': 'o', '\u0440': 'p', '\u0441': 'c',
|
|
24
|
+
'\u0443': 'y', '\u0445': 'x', '\u0456': 'i', '\u0458': 'j', '\u04BB': 'h',
|
|
25
|
+
'\u0261': 'g', '\u03B1': 'a', '\u03BF': 'o', '\u03B5': 'e',
|
|
26
|
+
// Fullwidth Latin
|
|
27
|
+
'\uFF41': 'a', '\uFF42': 'b', '\uFF43': 'c', '\uFF44': 'd', '\uFF45': 'e',
|
|
28
|
+
'\uFF46': 'f', '\uFF47': 'g', '\uFF48': 'h', '\uFF49': 'i', '\uFF4A': 'j',
|
|
29
|
+
'\uFF4B': 'k', '\uFF4C': 'l', '\uFF4D': 'm', '\uFF4E': 'n', '\uFF4F': 'o',
|
|
30
|
+
'\uFF50': 'p', '\uFF51': 'q', '\uFF52': 'r', '\uFF53': 's', '\uFF54': 't',
|
|
31
|
+
'\uFF55': 'u', '\uFF56': 'v', '\uFF57': 'w', '\uFF58': 'x', '\uFF59': 'y',
|
|
32
|
+
'\uFF5A': 'z',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function normalizeText(input: string): string {
|
|
36
|
+
let text = input;
|
|
37
|
+
text = text.replace(INVISIBLE_CHARS, '');
|
|
38
|
+
text = text.replace(/./g, (ch) => CONFUSABLE_MAP[ch] ?? ch);
|
|
39
|
+
text = text.toLowerCase();
|
|
40
|
+
text = text.replace(/[013457@$!]/g, (ch) => LEET_MAP[ch] ?? ch);
|
|
41
|
+
text = text.replace(/\b([a-z])(?:\s+[a-z]){2,}\b/g, (match) =>
|
|
42
|
+
match.replace(/\s+/g, '')
|
|
43
|
+
);
|
|
44
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Layer 2: Multi-category pattern bank ─────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const OVERRIDE_VERBS = '(?:ignore|override|bypass|skip|circumvent|disregard|forget|violate|break|disable|remove|deactivate|nullify|suspend|suppress|eliminate|drop|abandon|dismiss|disobey)';
|
|
51
|
+
const NEGATION_VERBS = '(?:do not follow|don\'t follow|stop following|stop enforcing|never follow|never enforce|cease following|quit following)';
|
|
52
|
+
const SAFETY_NOUNS = '(?:system|policy|policies|rules?|approval|permissions?|restrictions?|guidelines?|constraints?|guardrails?|instructions?|authorization|gating|safety|safeguards?|boundaries|limits?|protections?)';
|
|
53
|
+
|
|
54
|
+
const DISCLOSURE_VERBS = '(?:reveal|expose|leak|dump|show|output|print|display|disclose|share|repeat back|read out|recite|reproduce|echo|emit|return|give me)';
|
|
55
|
+
const SECRET_NOUNS = '(?:secret|token|api key|private memory|system prompt|initial prompt|hidden instructions?|internal state|configuration|prompt text|original instructions?|base prompt)';
|
|
56
|
+
|
|
57
|
+
const ESCALATION_PHRASES = [
|
|
58
|
+
/\b(?:developer|admin|unrestricted|unfiltered|unlimited|god|root|sudo|superuser)\s*mode\b/,
|
|
59
|
+
/\b(?:jailbreak|jail break|jailbroken)\b/,
|
|
60
|
+
/\byou are now\b.{0,40}\b(?:unrestricted|unfiltered|without limits|without restrictions|without rules|without guidelines|free from)\b/,
|
|
61
|
+
/\byour (?:new|real|actual|true)\s+(?:instructions?|prompt|rules?|policy|guidelines?)\b/,
|
|
62
|
+
/\bfrom now on you (?:will|must|should|shall)\b.{0,60}\b(?:obey|comply|follow all|do whatever|do anything|never refuse)\b/,
|
|
63
|
+
/\bpretend\b.{0,40}\b(?:no restrictions|no rules|no limits|no guidelines|no constraints|unrestricted|unfiltered)\b/,
|
|
64
|
+
/\bact as if\b.{0,40}\b(?:permissions?|restrictions?|rules?|policy|guidelines?|constraints?)\b.{0,20}\b(?:don't|do not|doesn't|does not|did not|didn't)\b/,
|
|
65
|
+
/\byou have\s+(?:full|complete|total|unlimited|unrestricted)\s+(?:\w+\s+)?(?:access|permissions?|authorization|control)\b/,
|
|
66
|
+
/\btreat\s+(?:all|every)\b.{0,40}\b(?:operator|admin|owner|administrator)\b/,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const APPROVAL_TAMPERING = [
|
|
70
|
+
/\b(?:auto(?:-|\s)?approve|automatically approve|always approve|approve\s+all|approve\s+every)\b/,
|
|
71
|
+
/\b(?:always accept|never reject|never deny|never refuse|skip\s+approval|bypass\s+approval)\b/,
|
|
72
|
+
/\b(?:treat as approved|mark as active|activate all|force activate|directly activate)\b/,
|
|
73
|
+
/\b(?:without\s+(?:approval|review|verification|confirmation|checking|authorization))\b/,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const SAFETY_DISABLE = [
|
|
77
|
+
/\b(?:turn off|shut off|switch off|power off|deactivate|disable|remove|delete|erase|clear|wipe|purge)\b.{0,30}\b(?:safety|security|filter|filtering|moderation|censorship|guardrails?)\b/,
|
|
78
|
+
/\b(?:no\s+(?:safety|security|content)\s+(?:filter|filtering|check|checks|moderation|restrictions))\b/,
|
|
79
|
+
/\b(?:without\s+(?:safety|security|content)\s+(?:filter|filtering|check|checks|moderation|restrictions))\b/,
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const OVERRIDE_PATTERN = new RegExp(`\\b${OVERRIDE_VERBS}\\b.{0,60}\\b${SAFETY_NOUNS}\\b`, 'i');
|
|
83
|
+
const NEGATION_PATTERN = new RegExp(`\\b${NEGATION_VERBS}\\b.{0,60}\\b${SAFETY_NOUNS}\\b`, 'i');
|
|
84
|
+
const DISCLOSURE_PATTERN = new RegExp(`\\b${DISCLOSURE_VERBS}\\b.{0,60}\\b${SECRET_NOUNS}\\b`, 'i');
|
|
85
|
+
|
|
86
|
+
// ── Layer 3: Structural heuristics ───────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
const STRUCTURAL_HEURISTICS = [
|
|
89
|
+
{ pattern: /\byour\s+new\s+(?:system\s+)?(?:policy|rules?|instructions?|guidelines?|prompt)\s+(?:is|are|will be|should be)\b/i, reason: 'policy_redefinition' },
|
|
90
|
+
{ pattern: /\b(?:when|if|after|once)\b.{0,60}\b(?:ignore|stop|disable|bypass|override|forget|drop|abandon)\b.{0,40}\b(?:safety|rules?|policy|guidelines?|restrictions?|guardrails?)\b/i, reason: 'conditional_unsafe_trigger' },
|
|
91
|
+
{ pattern: /\b(?:respond|reply|answer|act|behave)\b.{0,30}\b(?:as if you were|as though you are|like you are|pretending to be)\b.{0,40}\b(?:unfiltered|unrestricted|evil|uncensored|without limits)\b/i, reason: 'identity_swap' },
|
|
92
|
+
{ pattern: /\b(?:obey\s+all|do\s+whatever|do\s+anything|comply\s+with\s+(?:any|all|every)|always\s+comply|never\s+refuse|never\s+decline|never\s+deny\s+a\s+request)\b/i, reason: 'blanket_obedience' },
|
|
93
|
+
{ pattern: /\b(?:execute|run|invoke|call|trigger)\b.{0,20}\b(?:any|all|every)\b.{0,30}\b(?:tool|command|action|function|operation)\b.{0,40}\b(?:without|no)\b.{0,20}\b(?:check|auth|approval|verification|restriction)\b/i, reason: 'unauthorized_execution' },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
export const DEFAULT_SAFETY_PATTERNS: SafetyPatternConfig = {
|
|
97
|
+
overridePattern: OVERRIDE_PATTERN,
|
|
98
|
+
negationPattern: NEGATION_PATTERN,
|
|
99
|
+
disclosurePattern: DISCLOSURE_PATTERN,
|
|
100
|
+
escalationPhrases: ESCALATION_PHRASES,
|
|
101
|
+
approvalTampering: APPROVAL_TAMPERING,
|
|
102
|
+
safetyDisable: SAFETY_DISABLE,
|
|
103
|
+
structuralHeuristics: STRUCTURAL_HEURISTICS,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export function scanDirective(raw: string, patterns: SafetyPatternConfig = DEFAULT_SAFETY_PATTERNS): ScanResult {
|
|
107
|
+
if (!raw || raw.trim() === '') {
|
|
108
|
+
return { safe: true };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const text = normalizeText(raw);
|
|
112
|
+
|
|
113
|
+
if (patterns.overridePattern.test(text)) {
|
|
114
|
+
return { safe: false, reason: 'unsafe_override' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (patterns.negationPattern.test(text)) {
|
|
118
|
+
return { safe: false, reason: 'unsafe_negation' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (patterns.disclosurePattern.test(text)) {
|
|
122
|
+
return { safe: false, reason: 'unsafe_disclosure' };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const pattern of patterns.escalationPhrases) {
|
|
126
|
+
if (pattern.test(text)) {
|
|
127
|
+
return { safe: false, reason: 'unsafe_escalation' };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const pattern of patterns.approvalTampering) {
|
|
132
|
+
if (pattern.test(text)) {
|
|
133
|
+
return { safe: false, reason: 'unsafe_approval_tampering' };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const pattern of patterns.safetyDisable) {
|
|
138
|
+
if (pattern.test(text)) {
|
|
139
|
+
return { safe: false, reason: 'unsafe_safety_disable' };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const { pattern, reason } of patterns.structuralHeuristics) {
|
|
144
|
+
if (pattern.test(text)) {
|
|
145
|
+
return { safe: false, reason };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { safe: true };
|
|
150
|
+
}
|