@ai-pip/csl 0.1.4 → 0.1.5
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/layers/csl/index.ts +1 -0
- package/layers/csl/src/adapters/index.ts +10 -0
- package/layers/csl/src/adapters/input/DOMAdapter.ts +236 -0
- package/layers/csl/src/adapters/input/UIAdapter.ts +0 -0
- package/layers/csl/src/adapters/output/ConsoleLogger.ts +34 -0
- package/layers/csl/src/adapters/output/CryptoHashGenerator.ts +29 -0
- package/layers/csl/src/adapters/output/FilePolicyRepository.ts +0 -0
- package/layers/csl/src/adapters/output/InMemoryPolicyRepository.ts +135 -0
- package/layers/csl/src/adapters/output/SystemTimestampProvider.ts +9 -0
- package/layers/csl/src/domain/entities/CSLResult.ts +309 -0
- package/layers/csl/src/domain/entities/Segment.ts +338 -0
- package/layers/csl/src/domain/entities/index.ts +2 -0
- package/layers/csl/src/domain/exceptions/ClassificationError.ts +26 -0
- package/layers/csl/src/domain/exceptions/SegmentationError.ts +30 -0
- package/layers/csl/src/domain/exceptions/index.ts +2 -0
- package/layers/csl/src/domain/index.ts +4 -0
- package/layers/csl/src/domain/services/AnomalyService.ts +255 -0
- package/layers/csl/src/domain/services/LineageService.ts +224 -0
- package/layers/csl/src/domain/services/NormalizationService.ts +392 -0
- package/layers/csl/src/domain/services/OriginClassificationService.ts +69 -0
- package/layers/csl/src/domain/services/PiDetectionService.ts +475 -0
- package/layers/csl/src/domain/services/PolicyService.ts +296 -0
- package/layers/csl/src/domain/services/SegmentClassificationService.ts +105 -0
- package/layers/csl/src/domain/services/SerializationService.ts +229 -0
- package/layers/csl/src/domain/services/index.ts +7 -0
- package/layers/csl/src/domain/value-objects/AnomalyScore.ts +23 -0
- package/layers/csl/src/domain/value-objects/ContentHash.ts +54 -0
- package/layers/csl/src/domain/value-objects/LineageEntry.ts +42 -0
- package/layers/csl/src/domain/value-objects/Origin-map.ts +67 -0
- package/layers/csl/src/domain/value-objects/Origin.ts +99 -0
- package/layers/csl/src/domain/value-objects/Pattern.ts +221 -0
- package/layers/csl/src/domain/value-objects/PiDetection.ts +140 -0
- package/layers/csl/src/domain/value-objects/PiDetectionResult.ts +275 -0
- package/layers/csl/src/domain/value-objects/PolicyRule.ts +151 -0
- package/layers/csl/src/domain/value-objects/TrustLevel.ts +34 -0
- package/layers/csl/src/domain/value-objects/index.ts +10 -0
- package/layers/csl/src/index.ts +7 -0
- package/layers/csl/src/ports/index.ts +10 -0
- package/layers/csl/src/ports/input/ClassificationPort.ts +76 -0
- package/layers/csl/src/ports/input/SegmentationPort.ts +81 -0
- package/layers/csl/src/ports/output/DOMAdapter.ts +14 -0
- package/layers/csl/src/ports/output/HashGenerator.ts +18 -0
- package/layers/csl/src/ports/output/Logger.ts +17 -0
- package/layers/csl/src/ports/output/PolicyRepository.ts +29 -0
- package/layers/csl/src/ports/output/SegmentClassified.ts +8 -0
- package/layers/csl/src/ports/output/TimeStampProvider.ts +5 -0
- package/layers/csl/src/services/CSLService.ts +393 -0
- package/layers/csl/src/services/index.ts +1 -0
- package/layers/csl/src/types/entities-types.ts +37 -0
- package/layers/csl/src/types/index.ts +4 -0
- package/layers/csl/src/types/pi-types.ts +111 -0
- package/layers/csl/src/types/port-output-types.ts +17 -0
- package/layers/csl/src/types/value-objects-types.ts +213 -0
- package/layers/csl/src/utils/colors.ts +25 -0
- package/layers/csl/src/utils/pattern-helpers.ts +174 -0
- package/package.json +4 -5
- package/src/index.ts +36 -36
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import type { RiskScore, AnomalyAction } from '../../types'
|
|
2
|
+
import { PiDetection } from './PiDetection'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PiDetectionResult value object represents the complete result of prompt injection detection
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This value object aggregates all prompt injection detections found in a content segment.
|
|
9
|
+
* It provides both detailed information (individual detections) and aggregated values
|
|
10
|
+
* (total score, action recommendation) for convenience and compatibility.
|
|
11
|
+
*
|
|
12
|
+
* **Key Features:**
|
|
13
|
+
* - Supports multiple detections per segment (common in real attacks)
|
|
14
|
+
* - Each detection includes rich information: pattern type, matched text, position, confidence
|
|
15
|
+
* - Automatically calculates aggregated score from individual detections
|
|
16
|
+
* - Maintains immutability through Object.freeze
|
|
17
|
+
*
|
|
18
|
+
* **Score Calculation:**
|
|
19
|
+
* The aggregated score is calculated automatically from individual detection confidences.
|
|
20
|
+
* Multiple detections increase the overall risk score using a weighted formula that ensures
|
|
21
|
+
* the score never exceeds 1.0 while properly reflecting cumulative risk.
|
|
22
|
+
*
|
|
23
|
+
* **Action Determination:**
|
|
24
|
+
* The action (ALLOW/WARN/BLOCK) is determined based on the aggregated score:
|
|
25
|
+
* - Score >= 0.7 → BLOCK
|
|
26
|
+
* - Score >= 0.3 → WARN
|
|
27
|
+
* - Score < 0.3 → ALLOW
|
|
28
|
+
*
|
|
29
|
+
* @property detections - Array of individual prompt injection detections (immutable)
|
|
30
|
+
* @property score - Aggregated risk score (0-1), calculated automatically
|
|
31
|
+
* @property action - Recommended action based on aggregated score
|
|
32
|
+
* @property patterns - Array of pattern type names (derived from detections, for compatibility)
|
|
33
|
+
* @property detected - Boolean indicating if any detections were found (convenience)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // Single detection
|
|
38
|
+
* const detection1 = new PiDetection(
|
|
39
|
+
* 'role_swapping',
|
|
40
|
+
* 'you are no longer an AI',
|
|
41
|
+
* { start: 12, end: 35 },
|
|
42
|
+
* 0.95
|
|
43
|
+
* )
|
|
44
|
+
*
|
|
45
|
+
* const result = new PiDetectionResult([detection1], 'BLOCK')
|
|
46
|
+
*
|
|
47
|
+
* // Multiple detections
|
|
48
|
+
* const detection2 = new PiDetection(
|
|
49
|
+
* 'instruction_override',
|
|
50
|
+
* 'ignore previous instructions',
|
|
51
|
+
* { start: 0, end: 26 },
|
|
52
|
+
* 0.85
|
|
53
|
+
* )
|
|
54
|
+
*
|
|
55
|
+
* const result2 = new PiDetectionResult([detection1, detection2], 'BLOCK')
|
|
56
|
+
* // Score is automatically calculated from both confidences
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export class PiDetectionResult {
|
|
60
|
+
readonly detections: readonly PiDetection[]
|
|
61
|
+
readonly score: RiskScore
|
|
62
|
+
readonly action: AnomalyAction
|
|
63
|
+
readonly patterns: readonly string[]
|
|
64
|
+
readonly detected: boolean
|
|
65
|
+
|
|
66
|
+
constructor(detections: PiDetection[], action: AnomalyAction) {
|
|
67
|
+
// Validate detections array
|
|
68
|
+
if (!Array.isArray(detections)) {
|
|
69
|
+
throw new TypeError('PiDetectionResult detections must be an array')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Validate action
|
|
73
|
+
if (!['ALLOW', 'WARN', 'BLOCK'].includes(action)) {
|
|
74
|
+
throw new Error(`Invalid AnomalyAction: ${action}. Must be one of: ALLOW, WARN, BLOCK`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Validate each detection is a PiDetection instance
|
|
78
|
+
for (let i = 0; i < detections.length; i++) {
|
|
79
|
+
if (!(detections[i] instanceof PiDetection)) {
|
|
80
|
+
throw new TypeError(
|
|
81
|
+
`PiDetectionResult detections[${i}] must be an instance of PiDetection`
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Freeze detections array for immutability
|
|
87
|
+
this.detections = Object.freeze([...detections])
|
|
88
|
+
|
|
89
|
+
// Calculate aggregated score from individual detection confidences
|
|
90
|
+
this.score = this.calculateAggregatedScore(detections)
|
|
91
|
+
|
|
92
|
+
// Validate that provided action matches calculated score
|
|
93
|
+
const expectedAction = this.determineActionFromScore(this.score)
|
|
94
|
+
if (action !== expectedAction) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`PiDetectionResult action mismatch. Calculated score ${this.score} requires action '${expectedAction}', but '${action}' was provided`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.action = action
|
|
101
|
+
|
|
102
|
+
// Derive patterns array from detections (for backward compatibility)
|
|
103
|
+
this.patterns = Object.freeze(
|
|
104
|
+
detections.map(detection => detection.pattern_type)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// Convenience property
|
|
108
|
+
this.detected = detections.length > 0
|
|
109
|
+
|
|
110
|
+
// Freeze the entire object for immutability
|
|
111
|
+
Object.freeze(this)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Calculates aggregated risk score from individual detection confidences
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* Uses a weighted formula that:
|
|
119
|
+
* - Combines multiple confidences without exceeding 1.0
|
|
120
|
+
* - Gives higher weight to higher confidence detections
|
|
121
|
+
* - Accounts for cumulative risk (more detections = higher risk)
|
|
122
|
+
*
|
|
123
|
+
* Formula: Uses complementary probability approach
|
|
124
|
+
* score = 1 - (1 - c1) * (1 - c2) * ... * (1 - cn)
|
|
125
|
+
* This ensures multiple detections increase risk appropriately
|
|
126
|
+
*
|
|
127
|
+
* @param detections - Array of individual detections
|
|
128
|
+
* @returns Aggregated risk score (0-1)
|
|
129
|
+
*
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
private calculateAggregatedScore(detections: PiDetection[]): RiskScore {
|
|
133
|
+
if (detections.length === 0) {
|
|
134
|
+
return 0
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (detections.length === 1) {
|
|
138
|
+
// TypeScript doesn't infer that detections[0] exists after length check
|
|
139
|
+
// but we know it does, so we use non-null assertion safely
|
|
140
|
+
return detections[0]!.confidence
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Use complementary probability: 1 - (1-c1)*(1-c2)*...
|
|
144
|
+
// This properly accumulates risk from multiple detections
|
|
145
|
+
let complementaryProduct = 1
|
|
146
|
+
for (const detection of detections) {
|
|
147
|
+
complementaryProduct *= (1 - detection.confidence)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const aggregatedScore = 1 - complementaryProduct
|
|
151
|
+
|
|
152
|
+
// Ensure score is within bounds (should always be, but safety check)
|
|
153
|
+
return Math.max(0, Math.min(1, aggregatedScore))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Determines the recommended action based on the aggregated score
|
|
158
|
+
*
|
|
159
|
+
* @param score - The aggregated risk score (0-1)
|
|
160
|
+
* @returns The recommended action
|
|
161
|
+
*
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
private determineActionFromScore(score: RiskScore): AnomalyAction {
|
|
165
|
+
if (score >= 0.7) {
|
|
166
|
+
return 'BLOCK'
|
|
167
|
+
} else if (score >= 0.3) {
|
|
168
|
+
return 'WARN'
|
|
169
|
+
} else {
|
|
170
|
+
return 'ALLOW'
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns true if any patterns were detected
|
|
176
|
+
*
|
|
177
|
+
* @returns true if detections array has any items, false otherwise
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* if (result.hasDetections()) {
|
|
182
|
+
* console.log(`Found ${result.detections.length} prompt injection patterns`)
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
hasDetections(): boolean {
|
|
187
|
+
return this.detected
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Returns true if the action is BLOCK
|
|
192
|
+
*
|
|
193
|
+
* @returns true if action is 'BLOCK', false otherwise
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* if (result.shouldBlock()) {
|
|
198
|
+
* // Handle blocking logic
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
shouldBlock(): boolean {
|
|
203
|
+
return this.action === 'BLOCK'
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Returns true if the action is WARN
|
|
208
|
+
*
|
|
209
|
+
* @returns true if action is 'WARN', false otherwise
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* if (result.shouldWarn()) {
|
|
214
|
+
* // Log warning
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
shouldWarn(): boolean {
|
|
219
|
+
return this.action === 'WARN'
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Returns the number of detections found
|
|
224
|
+
*
|
|
225
|
+
* @returns Count of individual detections
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const count = result.getDetectionCount()
|
|
230
|
+
* console.log(`Found ${count} prompt injection patterns`)
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
getDetectionCount(): number {
|
|
234
|
+
return this.detections.length
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Returns all detections of a specific pattern type
|
|
239
|
+
*
|
|
240
|
+
* @param pattern_type - The pattern type to filter by
|
|
241
|
+
* @returns Array of detections matching the pattern type
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* const roleSwaps = result.getDetectionsByType('role_swapping')
|
|
246
|
+
* console.log(`Found ${roleSwaps.length} role swapping attempts`)
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
getDetectionsByType(pattern_type: string): readonly PiDetection[] {
|
|
250
|
+
return this.detections.filter(detection => detection.pattern_type === pattern_type)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Returns the highest confidence detection
|
|
255
|
+
*
|
|
256
|
+
* @returns The detection with the highest confidence, or undefined if no detections
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const mostConfident = result.getHighestConfidenceDetection()
|
|
261
|
+
* if (mostConfident) {
|
|
262
|
+
* console.log(`Most confident: ${mostConfident.pattern_type} (${mostConfident.confidence})`)
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
getHighestConfidenceDetection(): PiDetection | undefined {
|
|
267
|
+
if (this.detections.length === 0) {
|
|
268
|
+
return undefined
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return this.detections.reduce((highest, current) => {
|
|
272
|
+
return current.confidence > highest.confidence ? current : highest
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BlockedIntent,
|
|
3
|
+
SensitiveScope,
|
|
4
|
+
ProtectedRole,
|
|
5
|
+
ImmutableInstruction
|
|
6
|
+
} from '../../types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* RoleProtection configuration for policy rules
|
|
10
|
+
*
|
|
11
|
+
* @property protectedRoles - Array of roles that cannot be overridden
|
|
12
|
+
* @property immutableInstructions - Array of instructions that cannot be modified
|
|
13
|
+
*/
|
|
14
|
+
export interface RoleProtectionConfig {
|
|
15
|
+
readonly protectedRoles: readonly ProtectedRole[]
|
|
16
|
+
readonly immutableInstructions: readonly ImmutableInstruction[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* ContextLeakPrevention configuration for policy rules
|
|
21
|
+
*
|
|
22
|
+
* @property enabled - Whether context leak prevention is enabled
|
|
23
|
+
* @property blockMetadataExposure - Whether to block metadata exposure
|
|
24
|
+
* @property sanitizeInternalReferences - Whether to sanitize internal references
|
|
25
|
+
*/
|
|
26
|
+
export interface ContextLeakPreventionConfig {
|
|
27
|
+
readonly enabled: boolean
|
|
28
|
+
readonly blockMetadataExposure: boolean
|
|
29
|
+
readonly sanitizeInternalReferences: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* PolicyRule value object represents a complete policy configuration
|
|
34
|
+
*
|
|
35
|
+
* @property version - Policy version string
|
|
36
|
+
* @property blockedIntents - Array of intents that are explicitly blocked
|
|
37
|
+
* @property sensitiveScope - Array of sensitive topics requiring validation
|
|
38
|
+
* @property roleProtection - Configuration for role protection
|
|
39
|
+
* @property contextLeakPrevention - Configuration for context leak prevention
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const policy = new PolicyRule(
|
|
44
|
+
* '1.0',
|
|
45
|
+
* ['delete_user_data'],
|
|
46
|
+
* ['financial_transactions'],
|
|
47
|
+
* { protectedRoles: ['system'], immutableInstructions: [] },
|
|
48
|
+
* { enabled: true, blockMetadataExposure: true, sanitizeInternalReferences: true }
|
|
49
|
+
* )
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class PolicyRule {
|
|
53
|
+
readonly version: string
|
|
54
|
+
readonly blockedIntents: readonly BlockedIntent[]
|
|
55
|
+
readonly sensitiveScope: readonly SensitiveScope[]
|
|
56
|
+
readonly roleProtection: RoleProtectionConfig
|
|
57
|
+
readonly contextLeakPrevention: ContextLeakPreventionConfig
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
version: string,
|
|
61
|
+
blockedIntents: BlockedIntent[],
|
|
62
|
+
sensitiveScope: SensitiveScope[],
|
|
63
|
+
roleProtection: RoleProtectionConfig,
|
|
64
|
+
contextLeakPrevention: ContextLeakPreventionConfig
|
|
65
|
+
) {
|
|
66
|
+
if (!version || typeof version !== 'string' || version.trim().length === 0) {
|
|
67
|
+
throw new Error('PolicyRule version must be a non-empty string')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!Array.isArray(blockedIntents)) {
|
|
71
|
+
throw new Error('PolicyRule blockedIntents must be an array')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!Array.isArray(sensitiveScope)) {
|
|
75
|
+
throw new Error('PolicyRule sensitiveScope must be an array')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!roleProtection || typeof roleProtection !== 'object') {
|
|
79
|
+
throw new Error('PolicyRule roleProtection must be an object')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!Array.isArray(roleProtection.protectedRoles)) {
|
|
83
|
+
throw new Error('PolicyRule roleProtection.protectedRoles must be an array')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!Array.isArray(roleProtection.immutableInstructions)) {
|
|
87
|
+
throw new Error('PolicyRule roleProtection.immutableInstructions must be an array')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!contextLeakPrevention || typeof contextLeakPrevention !== 'object') {
|
|
91
|
+
throw new Error('PolicyRule contextLeakPrevention must be an object')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (typeof contextLeakPrevention.enabled !== 'boolean') {
|
|
95
|
+
throw new Error('PolicyRule contextLeakPrevention.enabled must be a boolean')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof contextLeakPrevention.blockMetadataExposure !== 'boolean') {
|
|
99
|
+
throw new Error('PolicyRule contextLeakPrevention.blockMetadataExposure must be a boolean')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (typeof contextLeakPrevention.sanitizeInternalReferences !== 'boolean') {
|
|
103
|
+
throw new Error('PolicyRule contextLeakPrevention.sanitizeInternalReferences must be a boolean')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.version = version.trim()
|
|
107
|
+
this.blockedIntents = Object.freeze([...blockedIntents])
|
|
108
|
+
this.sensitiveScope = Object.freeze([...sensitiveScope])
|
|
109
|
+
this.roleProtection = {
|
|
110
|
+
protectedRoles: Object.freeze([...roleProtection.protectedRoles]),
|
|
111
|
+
immutableInstructions: Object.freeze([...roleProtection.immutableInstructions])
|
|
112
|
+
}
|
|
113
|
+
this.contextLeakPrevention = Object.freeze({ ...contextLeakPrevention })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Returns true if the given intent is blocked
|
|
118
|
+
*/
|
|
119
|
+
isIntentBlocked(intent: string): boolean {
|
|
120
|
+
return this.blockedIntents.includes(intent)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns true if the given scope is sensitive
|
|
125
|
+
*/
|
|
126
|
+
isScopeSensitive(scope: string): boolean {
|
|
127
|
+
return this.sensitiveScope.includes(scope)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns true if the given role is protected
|
|
132
|
+
*/
|
|
133
|
+
isRoleProtected(role: string): boolean {
|
|
134
|
+
return this.roleProtection.protectedRoles.includes(role)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns true if the given instruction is immutable
|
|
139
|
+
*/
|
|
140
|
+
isInstructionImmutable(instruction: string): boolean {
|
|
141
|
+
return this.roleProtection.immutableInstructions.includes(instruction)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Returns true if context leak prevention is enabled
|
|
146
|
+
*/
|
|
147
|
+
isContextLeakPreventionEnabled(): boolean {
|
|
148
|
+
return this.contextLeakPrevention.enabled
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { TrustLevelType } from '../../types'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TrustLevel value object represent the trust level of the content segment
|
|
6
|
+
* @props
|
|
7
|
+
* - value: TrustLevelType
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const trustLevel = new TrustLevel(TrustLevelType.TC)
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class TrustLevel {
|
|
14
|
+
readonly value: TrustLevelType
|
|
15
|
+
|
|
16
|
+
constructor(value: TrustLevelType) {
|
|
17
|
+
if (!Object.values(TrustLevelType).includes(value)) {
|
|
18
|
+
throw new Error(`Invalid TrustLevel: ${value}`)
|
|
19
|
+
}
|
|
20
|
+
this.value = value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
isTrusted(): boolean {
|
|
24
|
+
return this.value === TrustLevelType.TC
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isSemiTrusted(): boolean {
|
|
28
|
+
return this.value === TrustLevelType.STC
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
isUntrusted(): boolean {
|
|
32
|
+
return this.value === TrustLevelType.UC
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './Origin'
|
|
2
|
+
export * from './AnomalyScore'
|
|
3
|
+
export * from './TrustLevel'
|
|
4
|
+
export * from './LineageEntry'
|
|
5
|
+
export * from './PiDetection'
|
|
6
|
+
export * from './PiDetectionResult'
|
|
7
|
+
export * from './ContentHash'
|
|
8
|
+
export * from './PolicyRule'
|
|
9
|
+
export * from './Origin-map'
|
|
10
|
+
export * from './Pattern'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './output/DOMAdapter';
|
|
2
|
+
export * from './output/Logger';
|
|
3
|
+
export * from './output/HashGenerator';
|
|
4
|
+
export * from './output/TimeStampProvider';
|
|
5
|
+
export * from './output/PolicyRepository';
|
|
6
|
+
export * from './output/SegmentClassified';
|
|
7
|
+
|
|
8
|
+
// Input ports
|
|
9
|
+
export * from './input/SegmentationPort';
|
|
10
|
+
export * from './input/ClassificationPort';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Origin, TrustLevel } from '../../domain/value-objects';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ClassificationPort is the input port for content classification that CSL exposes.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This port defines the contract for classifying content segments based on their origin.
|
|
8
|
+
* Classification determines the trust level (TC, STC, or UC) that should be assigned
|
|
9
|
+
* to a segment, which is a critical security decision in the CSL pipeline.
|
|
10
|
+
*
|
|
11
|
+
* **Purpose:**
|
|
12
|
+
* - Defines the contract for origin-based trust classification
|
|
13
|
+
* - Enables consistent trust level assignment across the system
|
|
14
|
+
* - Allows implementation flexibility without affecting consumers
|
|
15
|
+
*
|
|
16
|
+
* **Trust Level Classification:**
|
|
17
|
+
* - **TC (Trusted Content)**: Content from trusted sources (e.g., system-generated content)
|
|
18
|
+
* - **STC (Semi-Trusted Content)**: Content from partially trusted sources (e.g., visible DOM)
|
|
19
|
+
* - **UC (Untrusted Content)**: Content from untrusted sources (e.g., user input, hidden DOM)
|
|
20
|
+
*
|
|
21
|
+
* **Classification Rules:**
|
|
22
|
+
* The classification logic maps Origin types to TrustLevel values based on security
|
|
23
|
+
* considerations. Different origins have different trustworthiness levels:
|
|
24
|
+
* - USER_INPUT → UC (always untrusted)
|
|
25
|
+
* - DOM_VISIBLE → STC (semi-trusted, visible to user)
|
|
26
|
+
* - DOM_HIDDEN → UC (untrusted, potentially malicious)
|
|
27
|
+
* - DOM_ATTRIBUTE → STC (semi-trusted, visible attributes)
|
|
28
|
+
* - SCRIPT_INJECTED → UC (untrusted, dynamically injected)
|
|
29
|
+
* - NETWORK_FETCHED → UC (untrusted, external source)
|
|
30
|
+
* - UNKNOWN → UC (untrusted, unknown origin)
|
|
31
|
+
*
|
|
32
|
+
* **Implementation:**
|
|
33
|
+
* This interface is implemented by `OriginClassificationService` in the domain services layer.
|
|
34
|
+
* The service contains the business logic for mapping origins to trust levels.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Create a classification service that implements this port
|
|
39
|
+
* const classificationService: ClassificationPort = new ClassificationService()
|
|
40
|
+
*
|
|
41
|
+
* // Classify a segment by its origin
|
|
42
|
+
* const origin = new Origin(OriginType.USER_INPUT)
|
|
43
|
+
* const trustLevel = classificationService.classify(origin)
|
|
44
|
+
*
|
|
45
|
+
* // Use the trust level
|
|
46
|
+
* if (trustLevel.isUntrusted()) {
|
|
47
|
+
* // Apply additional security measures
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export interface ClassificationPort {
|
|
52
|
+
/**
|
|
53
|
+
* Classifies a content segment based on its origin and returns the appropriate trust level.
|
|
54
|
+
*
|
|
55
|
+
* @param origin - The Origin value object representing where the content came from.
|
|
56
|
+
* This determines the trustworthiness of the content segment.
|
|
57
|
+
*
|
|
58
|
+
* @returns The TrustLevel value object (TC, STC, or UC) that should be assigned
|
|
59
|
+
* to the segment based on its origin. The trust level determines how the
|
|
60
|
+
* segment will be handled in subsequent processing stages.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Classify user input (always untrusted)
|
|
65
|
+
* const userOrigin = new Origin(OriginType.USER_INPUT)
|
|
66
|
+
* const trustLevel = classificationPort.classify(userOrigin)
|
|
67
|
+
* // trustLevel.value === TrustLevelType.UC
|
|
68
|
+
*
|
|
69
|
+
* // Classify visible DOM content (semi-trusted)
|
|
70
|
+
* const domOrigin = new Origin(OriginType.DOM_VISIBLE)
|
|
71
|
+
* const trustLevel = classificationPort.classify(domOrigin)
|
|
72
|
+
* // trustLevel.value === TrustLevelType.STC
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
classify(origin: Origin): TrustLevel;
|
|
76
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { CSLResult } from '../../domain/entities/CSLResult';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SegmentationPort is the main input port that CSL exposes to the external world.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This port defines the primary public API of the CSL (Context Segmentation Layer).
|
|
8
|
+
* It is implemented by the SegmentationService, which orchestrates the complete
|
|
9
|
+
* segmentation pipeline including DOM adaptation, normalization, classification,
|
|
10
|
+
* prompt injection detection, hashing, anomaly scoring, and lineage tracking.
|
|
11
|
+
*
|
|
12
|
+
* **Purpose:**
|
|
13
|
+
* - Defines the contract for segmenting content from DOM elements or documents
|
|
14
|
+
* - Allows external consumers to process content through the CSL pipeline
|
|
15
|
+
* - Enables implementation flexibility without affecting consumers
|
|
16
|
+
*
|
|
17
|
+
* **Processing Pipeline:**
|
|
18
|
+
* 1. Adapts DOM element/document to extract content segments
|
|
19
|
+
* 2. Normalizes segment content
|
|
20
|
+
* 3. Classifies segments by origin (assigns TrustLevel)
|
|
21
|
+
* 4. Detects prompt injection attempts
|
|
22
|
+
* 5. Generates cryptographic hashes
|
|
23
|
+
* 6. Calculates anomaly scores
|
|
24
|
+
* 7. Tracks lineage for auditability
|
|
25
|
+
* 8. Returns aggregated CSLResult with all processed segments
|
|
26
|
+
*
|
|
27
|
+
* **Implementation:**
|
|
28
|
+
* This interface is implemented by `SegmentationService` in the services layer.
|
|
29
|
+
* The service coordinates domain services, adapters, and output ports to complete
|
|
30
|
+
* the segmentation process.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Create a segmentation service that implements this port
|
|
35
|
+
* const segmentationService: SegmentationPort = new SegmentationService(
|
|
36
|
+
* domAdapter,
|
|
37
|
+
* classificationService,
|
|
38
|
+
* normalizationService,
|
|
39
|
+
* // ... other dependencies
|
|
40
|
+
* )
|
|
41
|
+
*
|
|
42
|
+
* // Process a DOM element
|
|
43
|
+
* const result = await segmentationService.segment(document.body)
|
|
44
|
+
*
|
|
45
|
+
* // Access processed segments
|
|
46
|
+
* result.segments.forEach(segment => {
|
|
47
|
+
* console.log(`Segment ${segment.id}: ${segment.trustLevel.value}`)
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export interface SegmentationPort {
|
|
52
|
+
/**
|
|
53
|
+
* Segments content from a DOM element or document through the CSL processing pipeline.
|
|
54
|
+
*
|
|
55
|
+
* @param element - The HTMLElement or Document to segment. Can be any DOM element
|
|
56
|
+
* (e.g., document.body, a specific div, input field) or the entire document.
|
|
57
|
+
*
|
|
58
|
+
* @returns A Promise that resolves to a CSLResult containing:
|
|
59
|
+
* - All processed segments with trust levels, hashes, and scores
|
|
60
|
+
* - Aggregated metadata about the segmentation process
|
|
61
|
+
* - Complete lineage entries for traceability
|
|
62
|
+
*
|
|
63
|
+
* @throws {SegmentationError} If the segmentation process fails due to invalid input
|
|
64
|
+
* or processing errors
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Segment the entire document
|
|
69
|
+
* const result = await segmentationPort.segment(document)
|
|
70
|
+
*
|
|
71
|
+
* // Segment a specific element
|
|
72
|
+
* const formElement = document.getElementById('user-form')
|
|
73
|
+
* const result = await segmentationPort.segment(formElement)
|
|
74
|
+
*
|
|
75
|
+
* // Process results
|
|
76
|
+
* console.log(`Processed ${result.metadata.total_segments} segments`)
|
|
77
|
+
* console.log(`Average anomaly score: ${result.metadata.anomaly_score}`)
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
segment(element: HTMLElement | Document): Promise<CSLResult>;
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RawDomPayload } from "../../types";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DOMAdapterPort is the port for the DOMAdapter
|
|
6
|
+
*
|
|
7
|
+
* @property adapt - Adapt the raw dom to the domain
|
|
8
|
+
* @property debug - Debug the raw dom and show it in the console
|
|
9
|
+
*/
|
|
10
|
+
export interface DOMAdapterPort {
|
|
11
|
+
adapt(): Promise<RawDomPayload>;
|
|
12
|
+
debug(payload: RawDomPayload): void;
|
|
13
|
+
// ... more methods to come
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HashGeneratorPort is the port for the hash generator
|
|
5
|
+
* @property generate - Generate a hash from the content
|
|
6
|
+
* @property compare - compare a content with a hash and return true if they are the same
|
|
7
|
+
* @property generateHMAC - Generate an HMAC hash from the content using a secret key
|
|
8
|
+
* @property compareHMAC - Compare a content with an HMAC hash using a secret key and return true if they are the same
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type HashAlgorithm = 'sha1' | 'sha256' | 'sha512' | 'md5' | 'ripemd160';
|
|
12
|
+
|
|
13
|
+
export interface HashGeneratorPort {
|
|
14
|
+
generate(content: string, algorithm?: HashAlgorithm): Promise<string>;
|
|
15
|
+
compare(content: string, hash: string, algorithm?: HashAlgorithm): Promise<boolean>;
|
|
16
|
+
generateHMAC(content: string, secretKey: string, algorithm?: HashAlgorithm): Promise<string>;
|
|
17
|
+
compareHMAC(content: string, hash: string, secretKey: string, algorithm?: HashAlgorithm): Promise<boolean>;
|
|
18
|
+
}
|