@ai-pip/core 0.1.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 +201 -0
- package/README.md +165 -0
- package/package.json +79 -0
- package/src/cpe/envelope.ts +115 -0
- package/src/cpe/exceptions/EnvelopeError.ts +11 -0
- package/src/cpe/exceptions/index.ts +6 -0
- package/src/cpe/index.ts +35 -0
- package/src/cpe/types.ts +68 -0
- package/src/cpe/utils.ts +65 -0
- package/src/cpe/value-objects/Metadata.ts +78 -0
- package/src/cpe/value-objects/Nonce.ts +57 -0
- package/src/cpe/value-objects/Signature.ts +83 -0
- package/src/cpe/value-objects/index.ts +8 -0
- package/src/csl/classify.ts +77 -0
- package/src/csl/exceptions/ClassificationError.ts +16 -0
- package/src/csl/exceptions/SegmentationError.ts +19 -0
- package/src/csl/exceptions/index.ts +3 -0
- package/src/csl/index.ts +34 -0
- package/src/csl/lineage.ts +40 -0
- package/src/csl/segment.ts +100 -0
- package/src/csl/types.ts +113 -0
- package/src/csl/utils.ts +30 -0
- package/src/csl/value-objects/ContentHash.ts +48 -0
- package/src/csl/value-objects/LineageEntry.ts +33 -0
- package/src/csl/value-objects/Origin-map.ts +51 -0
- package/src/csl/value-objects/Origin.ts +52 -0
- package/src/csl/value-objects/TrustLevel.ts +33 -0
- package/src/csl/value-objects/index.ts +14 -0
- package/src/index.ts +18 -0
- package/src/isl/exceptions/SanitizationError.ts +14 -0
- package/src/isl/exceptions/index.ts +2 -0
- package/src/isl/index.ts +20 -0
- package/src/isl/sanitize.ts +93 -0
- package/src/isl/types.ts +87 -0
- package/src/isl/value-objects/AnomalyScore.ts +40 -0
- package/src/isl/value-objects/Pattern.ts +158 -0
- package/src/isl/value-objects/PiDetection.ts +92 -0
- package/src/isl/value-objects/PiDetectionResult.ts +129 -0
- package/src/isl/value-objects/PolicyRule.ts +117 -0
- package/src/isl/value-objects/index.ts +41 -0
- package/src/shared/index.ts +13 -0
- package/src/shared/lineage.ts +53 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { RiskScore } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pattern - tipo puro
|
|
5
|
+
*/
|
|
6
|
+
export type Pattern = {
|
|
7
|
+
readonly pattern_type: string
|
|
8
|
+
readonly regex: RegExp
|
|
9
|
+
readonly base_confidence: RiskScore
|
|
10
|
+
readonly description: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Constantes de seguridad para pattern matching
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_CONTENT_LENGTH = 10_000_000 // 10MB
|
|
17
|
+
export const MAX_PATTERN_LENGTH = 10_000
|
|
18
|
+
export const MAX_MATCHES = 10_000
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Valida pattern_type - función pura
|
|
22
|
+
*/
|
|
23
|
+
function validatePatternType(pattern_type: unknown): asserts pattern_type is string {
|
|
24
|
+
if (!pattern_type || typeof pattern_type !== 'string' || pattern_type.trim().length === 0) {
|
|
25
|
+
throw new TypeError('Pattern pattern_type must be a non-empty string')
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Valida regex - función pura
|
|
31
|
+
*/
|
|
32
|
+
function validateRegex(regex: unknown): asserts regex is string | RegExp {
|
|
33
|
+
if (!regex || (typeof regex !== 'string' && !(regex instanceof RegExp))) {
|
|
34
|
+
throw new TypeError('Pattern regex must be a string or a RegExp')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Valida base_confidence - función pura
|
|
40
|
+
*/
|
|
41
|
+
function validateBaseConfidence(base_confidence: unknown): asserts base_confidence is number {
|
|
42
|
+
if (typeof base_confidence !== 'number' || !Number.isFinite(base_confidence)) {
|
|
43
|
+
throw new TypeError('Pattern base_confidence must be a valid number')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (base_confidence < 0 || base_confidence > 1) {
|
|
47
|
+
throw new Error('Pattern base_confidence must be between 0 and 1')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Valida description - función pura
|
|
53
|
+
*/
|
|
54
|
+
function validateDescription(description: unknown): void {
|
|
55
|
+
if (description !== undefined && (typeof description !== 'string' || description.trim().length === 0)) {
|
|
56
|
+
throw new TypeError('Pattern description must be a non-empty string if provided')
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Compila regex string a RegExp - función pura
|
|
62
|
+
*/
|
|
63
|
+
function compileRegexString(regex: string): RegExp {
|
|
64
|
+
try {
|
|
65
|
+
return new RegExp(regex, 'i')
|
|
66
|
+
} catch (error) {
|
|
67
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
68
|
+
throw new TypeError(`Pattern regex must be a valid regular expression: ${regex}. Original error: ${errorMessage}`)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clona RegExp - función pura
|
|
74
|
+
*/
|
|
75
|
+
function cloneRegExp(regex: RegExp): RegExp {
|
|
76
|
+
return new RegExp(regex.source, regex.flags)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Crea un Pattern - función pura
|
|
81
|
+
*/
|
|
82
|
+
export function createPattern(
|
|
83
|
+
pattern_type: string,
|
|
84
|
+
regex: string | RegExp,
|
|
85
|
+
base_confidence: RiskScore,
|
|
86
|
+
description?: string
|
|
87
|
+
): Pattern {
|
|
88
|
+
// Validar inputs
|
|
89
|
+
validatePatternType(pattern_type)
|
|
90
|
+
validateRegex(regex)
|
|
91
|
+
validateBaseConfidence(base_confidence)
|
|
92
|
+
validateDescription(description)
|
|
93
|
+
|
|
94
|
+
// Validar y procesar regex
|
|
95
|
+
const regexSource = typeof regex === 'string' ? regex : regex.source
|
|
96
|
+
|
|
97
|
+
if (regexSource.length > MAX_PATTERN_LENGTH) {
|
|
98
|
+
throw new Error(`Pattern regex source exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Normalizar regex a RegExp
|
|
102
|
+
const normalizedRegex = typeof regex === 'string'
|
|
103
|
+
? compileRegexString(regex)
|
|
104
|
+
: cloneRegExp(regex)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
pattern_type: pattern_type.trim(),
|
|
108
|
+
regex: normalizedRegex,
|
|
109
|
+
base_confidence,
|
|
110
|
+
description: description?.trim() ?? ''
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Funciones puras para Pattern matching
|
|
116
|
+
*/
|
|
117
|
+
export function matchesPattern(pattern: Pattern, content: string): boolean {
|
|
118
|
+
if (!content || typeof content !== 'string') {
|
|
119
|
+
throw new TypeError('Pattern.matches: content must be a non-empty string')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (content.length > MAX_CONTENT_LENGTH) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Pattern.matches: Content length (${content.length}) exceeds maximum allowed length (${MAX_CONTENT_LENGTH})`
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return pattern.regex.test(content)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function findMatch(pattern: Pattern, content: string): {
|
|
132
|
+
matched: string
|
|
133
|
+
position: { start: number; end: number }
|
|
134
|
+
} | null {
|
|
135
|
+
if (!content || typeof content !== 'string') {
|
|
136
|
+
throw new TypeError('Pattern.findMatch: content must be a non-empty string')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (content.length > MAX_CONTENT_LENGTH) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Pattern.findMatch: Content length (${content.length}) exceeds maximum allowed length (${MAX_CONTENT_LENGTH})`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const match = pattern.regex.exec(content)
|
|
146
|
+
if (match?.index === undefined) {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
matched: match[0],
|
|
152
|
+
position: {
|
|
153
|
+
start: match.index,
|
|
154
|
+
end: match.index + match[0].length
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Position, RiskScore } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PiDetection - tipo puro
|
|
5
|
+
*/
|
|
6
|
+
export type PiDetection = {
|
|
7
|
+
readonly pattern_type: string
|
|
8
|
+
readonly matched_pattern: string
|
|
9
|
+
readonly position: Position
|
|
10
|
+
readonly confidence: RiskScore
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Crea un PiDetection - función pura
|
|
15
|
+
*/
|
|
16
|
+
export function createPiDetection(
|
|
17
|
+
pattern_type: string,
|
|
18
|
+
matched_pattern: string,
|
|
19
|
+
position: Position,
|
|
20
|
+
confidence: RiskScore
|
|
21
|
+
): PiDetection {
|
|
22
|
+
// Validar pattern_type
|
|
23
|
+
if (!pattern_type || typeof pattern_type !== 'string' || pattern_type.trim().length === 0) {
|
|
24
|
+
throw new Error('PiDetection pattern_type must be a non-empty string')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validar matched_pattern
|
|
28
|
+
if (!matched_pattern || typeof matched_pattern !== 'string') {
|
|
29
|
+
throw new Error('PiDetection matched_pattern must be a non-empty string')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validar position
|
|
33
|
+
if (!position || typeof position !== 'object') {
|
|
34
|
+
throw new TypeError('PiDetection position must be an object with start and end properties')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof position.start !== 'number' || !Number.isFinite(position.start) || position.start < 0) {
|
|
38
|
+
throw new Error('PiDetection position.start must be a valid non-negative number')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof position.end !== 'number' || !Number.isFinite(position.end) || position.end < 0) {
|
|
42
|
+
throw new Error('PiDetection position.end must be a valid non-negative number')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (position.end <= position.start) {
|
|
46
|
+
throw new Error('PiDetection position.end must be greater than position.start')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Validar confidence
|
|
50
|
+
if (typeof confidence !== 'number' || !Number.isFinite(confidence)) {
|
|
51
|
+
throw new TypeError('PiDetection confidence must be a valid number')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (confidence < 0 || confidence > 1) {
|
|
55
|
+
throw new Error('PiDetection confidence must be between 0 and 1')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Validar que matched_pattern length coincide con position
|
|
59
|
+
const patternLength = position.end - position.start
|
|
60
|
+
if (matched_pattern.length !== patternLength) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`PiDetection matched_pattern length (${matched_pattern.length}) does not match position range (${patternLength})`
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
pattern_type: pattern_type.trim(),
|
|
68
|
+
matched_pattern,
|
|
69
|
+
position: Object.freeze({ ...position }),
|
|
70
|
+
confidence
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Funciones puras para PiDetection
|
|
76
|
+
*/
|
|
77
|
+
export function getDetectionLength(detection: PiDetection): number {
|
|
78
|
+
return detection.position.end - detection.position.start
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isHighConfidence(detection: PiDetection): boolean {
|
|
82
|
+
return detection.confidence >= 0.7
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function isMediumConfidence(detection: PiDetection): boolean {
|
|
86
|
+
return detection.confidence >= 0.3 && detection.confidence < 0.7
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isLowConfidence(detection: PiDetection): boolean {
|
|
90
|
+
return detection.confidence < 0.3
|
|
91
|
+
}
|
|
92
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { AnomalyAction, RiskScore } from '../types'
|
|
2
|
+
import type { PiDetection } from './PiDetection'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PiDetectionResult - tipo puro
|
|
6
|
+
*/
|
|
7
|
+
export type PiDetectionResult = {
|
|
8
|
+
readonly detections: readonly PiDetection[]
|
|
9
|
+
readonly score: RiskScore
|
|
10
|
+
readonly action: AnomalyAction
|
|
11
|
+
readonly patterns: readonly string[]
|
|
12
|
+
readonly detected: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Calcula score agregado desde detecciones individuales - función pura
|
|
17
|
+
*/
|
|
18
|
+
function calculateAggregatedScore(detections: readonly PiDetection[]): RiskScore {
|
|
19
|
+
if (detections.length === 0) {
|
|
20
|
+
return 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (detections.length === 1) {
|
|
24
|
+
const first = detections[0]
|
|
25
|
+
if (!first) return 0
|
|
26
|
+
return first.confidence
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Usar probabilidad complementaria: 1 - (1-c1)*(1-c2)*...
|
|
30
|
+
let complementaryProduct = 1
|
|
31
|
+
for (const detection of detections) {
|
|
32
|
+
complementaryProduct *= (1 - detection.confidence)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const aggregatedScore = 1 - complementaryProduct
|
|
36
|
+
return Math.max(0, Math.min(1, aggregatedScore))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Determina acción basada en score - función pura
|
|
41
|
+
*/
|
|
42
|
+
function determineActionFromScore(score: RiskScore): AnomalyAction {
|
|
43
|
+
if (score >= 0.7) {
|
|
44
|
+
return 'BLOCK'
|
|
45
|
+
} else if (score >= 0.3) {
|
|
46
|
+
return 'WARN'
|
|
47
|
+
} else {
|
|
48
|
+
return 'ALLOW'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Crea un PiDetectionResult - función pura
|
|
54
|
+
*/
|
|
55
|
+
export function createPiDetectionResult(
|
|
56
|
+
detections: readonly PiDetection[],
|
|
57
|
+
action?: AnomalyAction
|
|
58
|
+
): PiDetectionResult {
|
|
59
|
+
// Validar detections array
|
|
60
|
+
if (!Array.isArray(detections)) {
|
|
61
|
+
throw new TypeError('PiDetectionResult detections must be an array')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Calcular score agregado
|
|
65
|
+
const score = calculateAggregatedScore(detections)
|
|
66
|
+
|
|
67
|
+
// Determinar acción (o usar la proporcionada)
|
|
68
|
+
const finalAction = action ?? determineActionFromScore(score)
|
|
69
|
+
|
|
70
|
+
// Validar acción
|
|
71
|
+
if (!['ALLOW', 'WARN', 'BLOCK'].includes(finalAction)) {
|
|
72
|
+
throw new Error(`Invalid AnomalyAction: ${finalAction}. Must be one of: ALLOW, WARN, BLOCK`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Validar que la acción coincide con el score calculado
|
|
76
|
+
const expectedAction = determineActionFromScore(score)
|
|
77
|
+
if (finalAction !== expectedAction) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`PiDetectionResult action mismatch. Calculated score ${score} requires action '${expectedAction}', but '${finalAction}' was provided`
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Derivar patterns array
|
|
84
|
+
const patterns: readonly string[] = detections.map((detection: PiDetection) => detection.pattern_type)
|
|
85
|
+
|
|
86
|
+
const result: PiDetectionResult = {
|
|
87
|
+
detections: Object.freeze(Array.from(detections)) as readonly PiDetection[],
|
|
88
|
+
score,
|
|
89
|
+
action: finalAction,
|
|
90
|
+
patterns: Object.freeze(Array.from(patterns)),
|
|
91
|
+
detected: detections.length > 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Funciones puras para PiDetectionResult
|
|
99
|
+
*/
|
|
100
|
+
export function hasDetections(result: PiDetectionResult): boolean {
|
|
101
|
+
return result.detected
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// shouldBlock y shouldWarn NO son core - son decisiones que van a ModelGateway
|
|
105
|
+
// El core solo produce señales (score, action), no decide acciones finales
|
|
106
|
+
|
|
107
|
+
export function getDetectionCount(result: PiDetectionResult): number {
|
|
108
|
+
return result.detections.length
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getDetectionsByType(
|
|
112
|
+
result: PiDetectionResult,
|
|
113
|
+
pattern_type: string
|
|
114
|
+
): readonly PiDetection[] {
|
|
115
|
+
return result.detections.filter(detection => detection.pattern_type === pattern_type)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getHighestConfidenceDetection(
|
|
119
|
+
result: PiDetectionResult
|
|
120
|
+
): PiDetection | undefined {
|
|
121
|
+
if (result.detections.length === 0) {
|
|
122
|
+
return undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return result.detections.reduce((highest, current) => {
|
|
126
|
+
return current.confidence > highest.confidence ? current : highest
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { BlockedIntent, ImmutableInstruction, ProtectedRole, SensitiveScope } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RoleProtectionConfig - configuración de protección de roles
|
|
5
|
+
*/
|
|
6
|
+
export type RoleProtectionConfig = {
|
|
7
|
+
readonly protectedRoles: readonly ProtectedRole[]
|
|
8
|
+
readonly immutableInstructions: readonly ImmutableInstruction[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ContextLeakPreventionConfig - configuración de prevención de fuga de contexto
|
|
13
|
+
*/
|
|
14
|
+
export type ContextLeakPreventionConfig = {
|
|
15
|
+
readonly enabled: boolean
|
|
16
|
+
readonly blockMetadataExposure: boolean
|
|
17
|
+
readonly sanitizeInternalReferences: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* PolicyRule - tipo puro
|
|
22
|
+
*/
|
|
23
|
+
export type PolicyRule = {
|
|
24
|
+
readonly version: string
|
|
25
|
+
readonly blockedIntents: readonly BlockedIntent[]
|
|
26
|
+
readonly sensitiveScope: readonly SensitiveScope[]
|
|
27
|
+
readonly roleProtection: RoleProtectionConfig
|
|
28
|
+
readonly contextLeakPrevention: ContextLeakPreventionConfig
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Crea un PolicyRule - función pura
|
|
33
|
+
*/
|
|
34
|
+
export function createPolicyRule(
|
|
35
|
+
version: string,
|
|
36
|
+
blockedIntents: readonly BlockedIntent[],
|
|
37
|
+
sensitiveScope: readonly SensitiveScope[],
|
|
38
|
+
roleProtection: RoleProtectionConfig,
|
|
39
|
+
contextLeakPrevention: ContextLeakPreventionConfig
|
|
40
|
+
): PolicyRule {
|
|
41
|
+
if (!version || typeof version !== 'string' || version.trim().length === 0) {
|
|
42
|
+
throw new Error('PolicyRule version must be a non-empty string')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!Array.isArray(blockedIntents)) {
|
|
46
|
+
throw new TypeError('PolicyRule blockedIntents must be an array')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!Array.isArray(sensitiveScope)) {
|
|
50
|
+
throw new TypeError('PolicyRule sensitiveScope must be an array')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!roleProtection || typeof roleProtection !== 'object') {
|
|
54
|
+
throw new TypeError('PolicyRule roleProtection must be an object')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!Array.isArray(roleProtection.protectedRoles)) {
|
|
58
|
+
throw new TypeError('PolicyRule roleProtection.protectedRoles must be an array')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!Array.isArray(roleProtection.immutableInstructions)) {
|
|
62
|
+
throw new TypeError('PolicyRule roleProtection.immutableInstructions must be an array')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!contextLeakPrevention || typeof contextLeakPrevention !== 'object') {
|
|
66
|
+
throw new TypeError('PolicyRule contextLeakPrevention must be an object')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof contextLeakPrevention.enabled !== 'boolean') {
|
|
70
|
+
throw new TypeError('PolicyRule contextLeakPrevention.enabled must be a boolean')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof contextLeakPrevention.blockMetadataExposure !== 'boolean') {
|
|
74
|
+
throw new TypeError('PolicyRule contextLeakPrevention.blockMetadataExposure must be a boolean')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (typeof contextLeakPrevention.sanitizeInternalReferences !== 'boolean') {
|
|
78
|
+
throw new TypeError('PolicyRule contextLeakPrevention.sanitizeInternalReferences must be a boolean')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result: PolicyRule = {
|
|
82
|
+
version: version.trim(),
|
|
83
|
+
blockedIntents: Object.freeze(Array.from(blockedIntents)) as readonly BlockedIntent[],
|
|
84
|
+
sensitiveScope: Object.freeze(Array.from(sensitiveScope)) as readonly SensitiveScope[],
|
|
85
|
+
roleProtection: {
|
|
86
|
+
protectedRoles: Object.freeze(Array.from(roleProtection.protectedRoles)) as readonly ProtectedRole[],
|
|
87
|
+
immutableInstructions: Object.freeze(Array.from(roleProtection.immutableInstructions)) as readonly ImmutableInstruction[]
|
|
88
|
+
},
|
|
89
|
+
contextLeakPrevention: Object.freeze({ ...contextLeakPrevention })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Funciones puras para PolicyRule
|
|
97
|
+
*/
|
|
98
|
+
export function isIntentBlocked(policy: PolicyRule, intent: string): boolean {
|
|
99
|
+
return policy.blockedIntents.includes(intent)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function isScopeSensitive(policy: PolicyRule, scope: string): boolean {
|
|
103
|
+
return policy.sensitiveScope.includes(scope)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isRoleProtected(policy: PolicyRule, role: string): boolean {
|
|
107
|
+
return policy.roleProtection.protectedRoles.includes(role)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function isInstructionImmutable(policy: PolicyRule, instruction: string): boolean {
|
|
111
|
+
return policy.roleProtection.immutableInstructions.includes(instruction)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function isContextLeakPreventionEnabled(policy: PolicyRule): boolean {
|
|
115
|
+
return policy.contextLeakPrevention.enabled
|
|
116
|
+
}
|
|
117
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Tipos
|
|
2
|
+
export type { PiDetection } from './PiDetection'
|
|
3
|
+
export type { PiDetectionResult } from './PiDetectionResult'
|
|
4
|
+
export type { AnomalyScore } from './AnomalyScore'
|
|
5
|
+
export type { Pattern } from './Pattern'
|
|
6
|
+
// Funciones de creación
|
|
7
|
+
export {
|
|
8
|
+
createPiDetection,
|
|
9
|
+
getDetectionLength,
|
|
10
|
+
isHighConfidence,
|
|
11
|
+
isMediumConfidence,
|
|
12
|
+
isLowConfidence
|
|
13
|
+
} from './PiDetection'
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
createPiDetectionResult,
|
|
17
|
+
hasDetections,
|
|
18
|
+
getDetectionCount,
|
|
19
|
+
getDetectionsByType,
|
|
20
|
+
getHighestConfidenceDetection
|
|
21
|
+
} from './PiDetectionResult'
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
createAnomalyScore,
|
|
25
|
+
isHighRisk,
|
|
26
|
+
isWarnRisk,
|
|
27
|
+
isLowRisk
|
|
28
|
+
} from './AnomalyScore'
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
createPattern,
|
|
32
|
+
matchesPattern,
|
|
33
|
+
findMatch,
|
|
34
|
+
MAX_CONTENT_LENGTH,
|
|
35
|
+
MAX_PATTERN_LENGTH,
|
|
36
|
+
MAX_MATCHES
|
|
37
|
+
} from './Pattern'
|
|
38
|
+
|
|
39
|
+
// PolicyRule NO es core - va a ModelGateway/Policy Engine
|
|
40
|
+
// Se mantiene el tipo para compatibilidad pero las funciones de decisión no son core
|
|
41
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for all layers - funciones puras compartidas
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Solo funciones básicas de manejo de linaje.
|
|
6
|
+
* Auditoría y análisis avanzado van al SDK.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Lineage básico
|
|
10
|
+
export * from './lineage'
|
|
11
|
+
|
|
12
|
+
// Funciones de auditoría NO son core - van al SDK/tooling
|
|
13
|
+
// El core solo preserva linaje, no lo analiza
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { LineageEntry } from '@/csl/value-objects'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lineage global - funciones puras para manejar linaje entre capas
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* El linaje es compartido entre todas las capas (CSL, ISL, CPE, etc.)
|
|
8
|
+
* Cada capa puede agregar entradas al linaje de un segmento.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Agrega una entrada de linaje a un array existente - función pura
|
|
13
|
+
*
|
|
14
|
+
* @param lineage - Array de entradas de linaje existentes
|
|
15
|
+
* @param entry - Nueva entrada a agregar
|
|
16
|
+
* @returns Nuevo array con la entrada agregada
|
|
17
|
+
*/
|
|
18
|
+
export function addLineageEntry(
|
|
19
|
+
lineage: readonly LineageEntry[],
|
|
20
|
+
entry: LineageEntry
|
|
21
|
+
): LineageEntry[] {
|
|
22
|
+
return [...lineage, entry]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Agrega múltiples entradas de linaje - función pura
|
|
27
|
+
*/
|
|
28
|
+
export function addLineageEntries(
|
|
29
|
+
lineage: readonly LineageEntry[],
|
|
30
|
+
entries: readonly LineageEntry[]
|
|
31
|
+
): LineageEntry[] {
|
|
32
|
+
return [...lineage, ...entries]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Filtra entradas de linaje por step - función pura
|
|
37
|
+
*/
|
|
38
|
+
export function filterLineageByStep(
|
|
39
|
+
lineage: readonly LineageEntry[],
|
|
40
|
+
step: string
|
|
41
|
+
): LineageEntry[] {
|
|
42
|
+
return lineage.filter(entry => entry.step === step)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Obtiene la última entrada de linaje - función pura
|
|
47
|
+
*/
|
|
48
|
+
export function getLastLineageEntry(
|
|
49
|
+
lineage: readonly LineageEntry[]
|
|
50
|
+
): LineageEntry | undefined {
|
|
51
|
+
return lineage.length > 0 ? lineage.at(-1) : undefined
|
|
52
|
+
}
|
|
53
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"baseUrl": ".",
|
|
9
|
+
"paths": {
|
|
10
|
+
"@/*": ["src/*"]
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
"strict": true,
|
|
14
|
+
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"emitDeclarationOnly": true,
|
|
17
|
+
"declarationMap": false,
|
|
18
|
+
|
|
19
|
+
"types": ["node"],
|
|
20
|
+
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
"exactOptionalPropertyTypes": true,
|
|
23
|
+
|
|
24
|
+
"skipLibCheck": true
|
|
25
|
+
},
|
|
26
|
+
"include": ["src/**/*", "test/**/*"]
|
|
27
|
+
}
|