@jterrats/open-orchestra 1.0.16 → 1.0.17
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/CLAUDE.md +24 -2
- package/README.md +32 -9
- package/dist/benchmark.js +65 -27
- package/dist/benchmark.js.map +1 -1
- package/dist/metrics-commands.js +3 -0
- package/dist/metrics-commands.js.map +1 -1
- package/dist/runtime-bootstrap.js +33 -8
- package/dist/runtime-bootstrap.js.map +1 -1
- package/dist/runtime-capacity-policy.d.ts +38 -0
- package/dist/runtime-capacity-policy.js +117 -0
- package/dist/runtime-capacity-policy.js.map +1 -0
- package/dist/runtime-capacity-scheduler-helpers.d.ts +40 -0
- package/dist/runtime-capacity-scheduler-helpers.js +111 -0
- package/dist/runtime-capacity-scheduler-helpers.js.map +1 -0
- package/dist/runtime-capacity-scheduler-state.d.ts +44 -0
- package/dist/runtime-capacity-scheduler-state.js +128 -0
- package/dist/runtime-capacity-scheduler-state.js.map +1 -0
- package/dist/runtime-capacity-scheduler.d.ts +34 -0
- package/dist/runtime-capacity-scheduler.js +193 -0
- package/dist/runtime-capacity-scheduler.js.map +1 -0
- package/dist/runtime-capacity-snapshot.d.ts +14 -0
- package/dist/runtime-capacity-snapshot.js +87 -0
- package/dist/runtime-capacity-snapshot.js.map +1 -0
- package/dist/runtime-child-prompt.d.ts +2 -1
- package/dist/runtime-child-prompt.js +4 -1
- package/dist/runtime-child-prompt.js.map +1 -1
- package/dist/runtime-claude-native-bridge.js +2 -1
- package/dist/runtime-claude-native-bridge.js.map +1 -1
- package/dist/runtime-commands.js +6 -0
- package/dist/runtime-commands.js.map +1 -1
- package/dist/runtime-lifecycle-watch.d.ts +5 -2
- package/dist/runtime-lifecycle-watch.js +19 -3
- package/dist/runtime-lifecycle-watch.js.map +1 -1
- package/dist/runtime-load-balancer.d.ts +12 -0
- package/dist/runtime-load-balancer.js +106 -0
- package/dist/runtime-load-balancer.js.map +1 -0
- package/dist/runtime-spawn-bridge.js +23 -0
- package/dist/runtime-spawn-bridge.js.map +1 -1
- package/dist/runtime-spawn-guidance.js +15 -0
- package/dist/runtime-spawn-guidance.js.map +1 -1
- package/dist/runtime-worker-registry.d.ts +19 -0
- package/dist/runtime-worker-registry.js +84 -0
- package/dist/runtime-worker-registry.js.map +1 -0
- package/dist/security/content-classifier.d.ts +2 -0
- package/dist/security/content-classifier.js +147 -0
- package/dist/security/content-classifier.js.map +1 -0
- package/dist/security/operation-contract-types.d.ts +28 -0
- package/dist/security/operation-contract-types.js +2 -0
- package/dist/security/operation-contract-types.js.map +1 -0
- package/dist/security/operation-contract.d.ts +2 -0
- package/dist/security/operation-contract.js +169 -0
- package/dist/security/operation-contract.js.map +1 -0
- package/dist/security/policy-engine.d.ts +2 -0
- package/dist/security/policy-engine.js +142 -0
- package/dist/security/policy-engine.js.map +1 -0
- package/dist/security/policy-types.d.ts +79 -0
- package/dist/security/policy-types.js +7 -0
- package/dist/security/policy-types.js.map +1 -0
- package/dist/security/prompt-intake.d.ts +13 -0
- package/dist/security/prompt-intake.js +33 -0
- package/dist/security/prompt-intake.js.map +1 -0
- package/dist/security/redaction.d.ts +3 -0
- package/dist/security/redaction.js +64 -0
- package/dist/security/redaction.js.map +1 -0
- package/dist/security/sink-encoding.d.ts +6 -0
- package/dist/security/sink-encoding.js +40 -0
- package/dist/security/sink-encoding.js.map +1 -0
- package/dist/sprint-commands.js +33 -22
- package/dist/sprint-commands.js.map +1 -1
- package/dist/transcription-failures.d.ts +2 -0
- package/dist/transcription-failures.js +4 -0
- package/dist/transcription-failures.js.map +1 -0
- package/dist/transcription-media-preflight.d.ts +9 -0
- package/dist/transcription-media-preflight.js +147 -0
- package/dist/transcription-media-preflight.js.map +1 -0
- package/dist/transcription-request.d.ts +13 -0
- package/dist/transcription-request.js +150 -0
- package/dist/transcription-request.js.map +1 -0
- package/dist/transcription-source-policy.d.ts +4 -0
- package/dist/transcription-source-policy.js +43 -0
- package/dist/transcription-source-policy.js.map +1 -0
- package/dist/transcription-types.d.ts +161 -0
- package/dist/transcription-types.js +2 -0
- package/dist/transcription-types.js.map +1 -0
- package/dist/types/runtime.d.ts +147 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/workflow-phase-planner.js +5 -3
- package/dist/workflow-phase-planner.js.map +1 -1
- package/dist/workflow-phases.js +5 -0
- package/dist/workflow-phases.js.map +1 -1
- package/dist/workflow-run-commands.js +89 -10
- package/dist/workflow-run-commands.js.map +1 -1
- package/docs/audio-video-transcription-skill.md +102 -70
- package/docs/runtime-adapters.md +7 -0
- package/docs/runtime-capacity.md +57 -0
- package/docs/security-saas-orchestrator.md +368 -0
- package/package.json +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { encodeForSink } from "./sink-encoding.js";
|
|
2
|
+
import { evaluateSecurityPolicy } from "./policy-engine.js";
|
|
3
|
+
import { intakePromptSegment } from "./prompt-intake.js";
|
|
4
|
+
import { redactPromptSegments } from "./redaction.js";
|
|
5
|
+
const specs = {
|
|
6
|
+
evidence: {
|
|
7
|
+
action: "evidence.write",
|
|
8
|
+
resourceType: "evidence",
|
|
9
|
+
sink: "evidence",
|
|
10
|
+
segmentKind: "evidence",
|
|
11
|
+
},
|
|
12
|
+
provider: {
|
|
13
|
+
action: "provider.message",
|
|
14
|
+
resourceType: "prompt",
|
|
15
|
+
sink: "provider",
|
|
16
|
+
segmentKind: "data",
|
|
17
|
+
},
|
|
18
|
+
pythonWorker: {
|
|
19
|
+
action: "pythonWorker.process",
|
|
20
|
+
resourceType: "pythonWorker",
|
|
21
|
+
sink: "json",
|
|
22
|
+
segmentKind: "data",
|
|
23
|
+
},
|
|
24
|
+
runtime: {
|
|
25
|
+
action: "command.execute",
|
|
26
|
+
resourceType: "command",
|
|
27
|
+
sink: "log",
|
|
28
|
+
segmentKind: "instruction",
|
|
29
|
+
},
|
|
30
|
+
tool: {
|
|
31
|
+
action: "command.execute",
|
|
32
|
+
resourceType: "command",
|
|
33
|
+
sink: "log",
|
|
34
|
+
segmentKind: "toolInput",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export function validateOperationPacket(input) {
|
|
38
|
+
const contractRules = validateContractShape(input);
|
|
39
|
+
if (contractRules.length > 0)
|
|
40
|
+
return contractDeny(input.packetId, contractRules);
|
|
41
|
+
const kind = input.kind;
|
|
42
|
+
const spec = specs[kind];
|
|
43
|
+
const packetId = input.packetId;
|
|
44
|
+
const subject = input.subject;
|
|
45
|
+
const action = input.action;
|
|
46
|
+
const resource = input.resource;
|
|
47
|
+
const sink = input.sink;
|
|
48
|
+
const semanticRules = [
|
|
49
|
+
exactRule("operation.contract.action", action, spec.action, "action"),
|
|
50
|
+
exactRule("operation.contract.resource", resource.resourceType, spec.resourceType, "resource type"),
|
|
51
|
+
exactRule("operation.contract.sink", sink, spec.sink, "sink"),
|
|
52
|
+
...validatePythonWorkerContract(input),
|
|
53
|
+
].filter((rule) => rule !== null);
|
|
54
|
+
if (semanticRules.length > 0)
|
|
55
|
+
return contractDeny(input.packetId, semanticRules);
|
|
56
|
+
const segment = normalizedSegment(input, spec, packetId, sink);
|
|
57
|
+
const redactionReport = redactPromptSegments([segment]);
|
|
58
|
+
const policySegment = redactedPolicySegment(input, spec, redactionReport, packetId, sink);
|
|
59
|
+
const policyDecision = evaluateSecurityPolicy({
|
|
60
|
+
requestId: packetId,
|
|
61
|
+
subject,
|
|
62
|
+
action,
|
|
63
|
+
resource,
|
|
64
|
+
sink,
|
|
65
|
+
dataClassification: policySegment.classification.classification,
|
|
66
|
+
segments: [policySegment],
|
|
67
|
+
redactionReport,
|
|
68
|
+
});
|
|
69
|
+
if (policyDecision.outcome === "deny")
|
|
70
|
+
return policyDecision;
|
|
71
|
+
return {
|
|
72
|
+
...policyDecision,
|
|
73
|
+
encodedPayload: encodeForSink(policySegment.text, sink).value,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function validateContractShape(input) {
|
|
77
|
+
return [
|
|
78
|
+
requiredRule("operation.contract.packet-id", "packet id", input.packetId),
|
|
79
|
+
requiredRule("operation.contract.kind", "operation kind", input.kind),
|
|
80
|
+
requiredRule("operation.contract.subject", "subject", input.subject),
|
|
81
|
+
requiredRule("operation.contract.action", "action", input.action),
|
|
82
|
+
requiredRule("operation.contract.resource", "resource", input.resource),
|
|
83
|
+
requiredRule("operation.contract.resource-type", "resource type", input.resource?.resourceType),
|
|
84
|
+
requiredRule("operation.contract.sink", "sink", input.sink),
|
|
85
|
+
requiredRule("operation.contract.payload", "payload", input.payload),
|
|
86
|
+
requiredRule("operation.contract.payload-text", "payload text", input.payload?.text),
|
|
87
|
+
].filter((rule) => rule !== null);
|
|
88
|
+
}
|
|
89
|
+
function validatePythonWorkerContract(input) {
|
|
90
|
+
if (input.kind !== "pythonWorker")
|
|
91
|
+
return [];
|
|
92
|
+
const worker = input.pythonWorker;
|
|
93
|
+
const rules = [
|
|
94
|
+
requiredRule("operation.python-worker.contract", "worker contract", worker),
|
|
95
|
+
exactRule("operation.python-worker.json-contract", worker?.contract, "json", "worker contract"),
|
|
96
|
+
falseRule("operation.python-worker.no-auth", "worker authorization authority", worker?.authorizes),
|
|
97
|
+
falseRule("operation.python-worker.no-network", "worker network access", worker?.network),
|
|
98
|
+
falseRule("operation.python-worker.no-filesystem", "worker filesystem access", worker?.filesystem),
|
|
99
|
+
boundedNumberRule("operation.python-worker.timeout", "worker timeout", worker?.timeoutMs, 1, 5000),
|
|
100
|
+
boundedNumberRule("operation.python-worker.input-bytes", "worker max input bytes", worker?.maxInputBytes, 1, 1_000_000),
|
|
101
|
+
jsonRule(input.payload?.text),
|
|
102
|
+
];
|
|
103
|
+
return rules.filter((rule) => rule !== null);
|
|
104
|
+
}
|
|
105
|
+
function normalizedSegment(input, spec, packetId, sink) {
|
|
106
|
+
return intakePromptSegment({
|
|
107
|
+
id: `${packetId}:payload`,
|
|
108
|
+
kind: input.payload?.kind ?? spec.segmentKind,
|
|
109
|
+
provenance: input.payload?.provenance ?? `${input.kind}:packet`,
|
|
110
|
+
sink,
|
|
111
|
+
text: input.payload?.text ?? "",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function redactedPolicySegment(input, spec, redactionReport, packetId, sink) {
|
|
115
|
+
return intakePromptSegment({
|
|
116
|
+
id: `${packetId}:payload:redacted`,
|
|
117
|
+
kind: input.payload?.kind ?? spec.segmentKind,
|
|
118
|
+
provenance: input.payload?.provenance ?? `${input.kind}:packet`,
|
|
119
|
+
sink,
|
|
120
|
+
text: redactionReport.redactedSegments[0]?.text ?? "",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function requiredRule(ruleId, label, value) {
|
|
124
|
+
if (value)
|
|
125
|
+
return null;
|
|
126
|
+
return { ruleId, reason: `missing ${label}` };
|
|
127
|
+
}
|
|
128
|
+
function exactRule(ruleId, actual, expected, label) {
|
|
129
|
+
if (actual === expected)
|
|
130
|
+
return null;
|
|
131
|
+
return { ruleId, reason: `ambiguous ${label}` };
|
|
132
|
+
}
|
|
133
|
+
function falseRule(ruleId, label, value) {
|
|
134
|
+
if (value === false)
|
|
135
|
+
return null;
|
|
136
|
+
return { ruleId, reason: `${label} must be disabled` };
|
|
137
|
+
}
|
|
138
|
+
function boundedNumberRule(ruleId, label, value, min, max) {
|
|
139
|
+
if (typeof value === "number" &&
|
|
140
|
+
Number.isInteger(value) &&
|
|
141
|
+
value >= min &&
|
|
142
|
+
value <= max) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return { ruleId, reason: `${label} must be between ${min} and ${max}` };
|
|
146
|
+
}
|
|
147
|
+
function jsonRule(text) {
|
|
148
|
+
try {
|
|
149
|
+
JSON.parse(text ?? "");
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return {
|
|
154
|
+
ruleId: "operation.python-worker.json-payload",
|
|
155
|
+
reason: "worker payload must be valid JSON",
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function contractDeny(packetId, rules) {
|
|
160
|
+
return {
|
|
161
|
+
requestId: packetId ?? "unknown",
|
|
162
|
+
outcome: "deny",
|
|
163
|
+
matchedRuleIds: rules.map((rule) => rule.ruleId),
|
|
164
|
+
redactionStatus: "unsafeUnredacted",
|
|
165
|
+
sanitizedReasons: rules.map((rule) => rule.reason),
|
|
166
|
+
evidenceSummary: "deny: operation contract failed closed",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=operation-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-contract.js","sourceRoot":"","sources":["../../src/security/operation-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const sensitiveSinks = ["evidence", "log", "provider"];
|
|
2
|
+
export function evaluateSecurityPolicy(input) {
|
|
3
|
+
const validation = validatePolicyRequest(input);
|
|
4
|
+
if (validation.length > 0) {
|
|
5
|
+
return denyDecision(input.requestId, validation, "unsafeUnredacted");
|
|
6
|
+
}
|
|
7
|
+
const request = input;
|
|
8
|
+
const denied = deniedRules(request);
|
|
9
|
+
if (denied.length > 0) {
|
|
10
|
+
return denyDecision(request.requestId, denied, highestRedactionStatus(denied, request.redactionReport.status));
|
|
11
|
+
}
|
|
12
|
+
const quarantined = quarantineRules(request);
|
|
13
|
+
if (quarantined.length > 0) {
|
|
14
|
+
return quarantineDecision(request.requestId, quarantined, request.redactionReport.status);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
requestId: request.requestId,
|
|
18
|
+
outcome: "allow",
|
|
19
|
+
matchedRuleIds: ["policy.default.allow-after-rules"],
|
|
20
|
+
redactionStatus: request.redactionReport.status,
|
|
21
|
+
sanitizedReasons: ["request satisfied deterministic policy rules"],
|
|
22
|
+
evidenceSummary: "allow: deterministic security policy accepted request",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function validatePolicyRequest(input) {
|
|
26
|
+
const missing = [
|
|
27
|
+
requiredRule("policy.input.request-id", "request id", input.requestId),
|
|
28
|
+
requiredRule("policy.input.subject", "subject", input.subject),
|
|
29
|
+
requiredRule("policy.input.action", "action", input.action),
|
|
30
|
+
requiredRule("policy.input.resource", "resource", input.resource),
|
|
31
|
+
requiredRule("policy.input.sink", "sink", input.sink),
|
|
32
|
+
requiredRule("policy.input.data-classification", "data classification", input.dataClassification),
|
|
33
|
+
requiredRule("policy.input.redaction", "redaction report", input.redactionReport),
|
|
34
|
+
].filter((rule) => rule !== null);
|
|
35
|
+
if (missing.length > 0)
|
|
36
|
+
return missing;
|
|
37
|
+
const scoped = input.subject?.tenantId ?? input.subject?.workspaceId;
|
|
38
|
+
const resourceScoped = input.resource?.tenantId ?? input.resource?.workspaceId;
|
|
39
|
+
const scopeRules = [
|
|
40
|
+
requiredRule("policy.scope.subject", "subject tenant or workspace", scoped),
|
|
41
|
+
requiredRule("policy.scope.resource", "resource tenant or workspace", resourceScoped),
|
|
42
|
+
].filter((rule) => rule !== null);
|
|
43
|
+
if (scopeRules.length > 0)
|
|
44
|
+
return scopeRules;
|
|
45
|
+
if (input.subject?.tenantId && input.resource?.tenantId) {
|
|
46
|
+
if (input.subject.tenantId !== input.resource.tenantId) {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
ruleId: "policy.scope.tenant-mismatch",
|
|
50
|
+
reason: "subject and resource tenant scope do not match",
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
function deniedRules(request) {
|
|
58
|
+
const rules = [];
|
|
59
|
+
if (request.redactionReport.status === "unsafeUnredacted") {
|
|
60
|
+
rules.push({
|
|
61
|
+
ruleId: "policy.redaction.fail-closed",
|
|
62
|
+
reason: "restricted content is not safely redacted",
|
|
63
|
+
redactionStatus: "unsafeUnredacted",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (request.action === "url.fetch" || request.sink === "url") {
|
|
67
|
+
rules.push(...findingRules(request, "unsafeUrl", "deny"));
|
|
68
|
+
}
|
|
69
|
+
if (request.action === "file.write") {
|
|
70
|
+
rules.push(...findingRules(request, "pathTraversal", "deny"));
|
|
71
|
+
}
|
|
72
|
+
return rules;
|
|
73
|
+
}
|
|
74
|
+
function quarantineRules(request) {
|
|
75
|
+
const rules = request.segments.flatMap((segment) => {
|
|
76
|
+
if (segment.kind === "unknown" && isSensitiveSink(request.sink)) {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
ruleId: "policy.segment.unknown-sensitive-sink",
|
|
80
|
+
reason: `segment ${segment.id} is unknown for sensitive sink`,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
return segment.classification.findings
|
|
85
|
+
.filter((finding) => finding.severity === "critical" || finding.severity === "high")
|
|
86
|
+
.map((finding) => findingRule(segment.id, finding, "quarantine"));
|
|
87
|
+
});
|
|
88
|
+
if (request.dataClassification === "unknown" &&
|
|
89
|
+
isSensitiveSink(request.sink)) {
|
|
90
|
+
rules.push({
|
|
91
|
+
ruleId: "policy.data.unknown-sensitive-sink",
|
|
92
|
+
reason: "unknown data classification cannot reach sensitive sink",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return rules;
|
|
96
|
+
}
|
|
97
|
+
function findingRules(request, kind, disposition) {
|
|
98
|
+
return request.segments.flatMap((segment) => segment.classification.findings
|
|
99
|
+
.filter((finding) => finding.kind === kind)
|
|
100
|
+
.map((finding) => findingRule(segment.id, finding, disposition)));
|
|
101
|
+
}
|
|
102
|
+
function findingRule(segmentId, finding, disposition) {
|
|
103
|
+
return {
|
|
104
|
+
ruleId: `policy.${disposition}.${finding.kind}`,
|
|
105
|
+
reason: `segment ${segmentId} matched ${finding.summary}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function requiredRule(ruleId, label, value) {
|
|
109
|
+
if (value)
|
|
110
|
+
return null;
|
|
111
|
+
return {
|
|
112
|
+
ruleId,
|
|
113
|
+
reason: `missing ${label}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function denyDecision(requestId, rules, redactionStatus) {
|
|
117
|
+
return {
|
|
118
|
+
requestId: requestId ?? "unknown",
|
|
119
|
+
outcome: "deny",
|
|
120
|
+
matchedRuleIds: rules.map((rule) => rule.ruleId),
|
|
121
|
+
redactionStatus,
|
|
122
|
+
sanitizedReasons: rules.map((rule) => rule.reason),
|
|
123
|
+
evidenceSummary: "deny: deterministic security policy failed closed",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function quarantineDecision(requestId, rules, redactionStatus) {
|
|
127
|
+
return {
|
|
128
|
+
requestId,
|
|
129
|
+
outcome: "quarantine",
|
|
130
|
+
matchedRuleIds: rules.map((rule) => rule.ruleId),
|
|
131
|
+
redactionStatus,
|
|
132
|
+
sanitizedReasons: rules.map((rule) => rule.reason),
|
|
133
|
+
evidenceSummary: "quarantine: deterministic security policy isolated content",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function highestRedactionStatus(rules, fallback) {
|
|
137
|
+
return (rules.find((rule) => rule.redactionStatus)?.redactionStatus ?? fallback);
|
|
138
|
+
}
|
|
139
|
+
function isSensitiveSink(sink) {
|
|
140
|
+
return sensitiveSinks.some((sensitiveSink) => sensitiveSink === sink);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=policy-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-engine.js","sourceRoot":"","sources":["../../src/security/policy-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export declare const policyDecisionOutcomes: readonly ["allow", "deny", "requiresApproval", "quarantine"];
|
|
2
|
+
export type PolicyDecisionOutcome = (typeof policyDecisionOutcomes)[number];
|
|
3
|
+
export type PolicyAction = "content.ingest" | "evidence.write" | "pythonWorker.process" | "provider.message" | "url.fetch" | "file.write" | "command.execute";
|
|
4
|
+
export type PolicySink = "evidence" | "htmlText" | "json" | "log" | "markdown" | "provider" | "shellArgument" | "url";
|
|
5
|
+
export type SegmentKind = "data" | "evidence" | "instruction" | "providerResponse" | "toolInput" | "toolOutput" | "unknown";
|
|
6
|
+
export type DataClassification = "public" | "internal" | "restricted" | "unknown";
|
|
7
|
+
export type FindingSeverity = "low" | "medium" | "high" | "critical";
|
|
8
|
+
export type ContentFindingKind = "indirectPromptInjection" | "noSqlLike" | "pathTraversal" | "promptInjection" | "secretShaped" | "shellLike" | "sqlLike" | "unsafeUrl";
|
|
9
|
+
export interface ContentFinding {
|
|
10
|
+
kind: ContentFindingKind;
|
|
11
|
+
ruleId: string;
|
|
12
|
+
severity: FindingSeverity;
|
|
13
|
+
summary: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ContentClassification {
|
|
16
|
+
classification: DataClassification;
|
|
17
|
+
findings: ContentFinding[];
|
|
18
|
+
}
|
|
19
|
+
export interface PromptSegment {
|
|
20
|
+
id: string;
|
|
21
|
+
kind: SegmentKind;
|
|
22
|
+
provenance: string;
|
|
23
|
+
sink: PolicySink;
|
|
24
|
+
text: string;
|
|
25
|
+
byteLength: number;
|
|
26
|
+
classification: ContentClassification;
|
|
27
|
+
}
|
|
28
|
+
export type RedactionStatus = "notRequired" | "redacted" | "quarantined" | "unsafeUnredacted";
|
|
29
|
+
export interface RedactedSegment {
|
|
30
|
+
id: string;
|
|
31
|
+
text: string;
|
|
32
|
+
status: RedactionStatus;
|
|
33
|
+
redactedFindings: ContentFindingKind[];
|
|
34
|
+
}
|
|
35
|
+
export interface RedactionReport {
|
|
36
|
+
status: RedactionStatus;
|
|
37
|
+
redactedSegments: RedactedSegment[];
|
|
38
|
+
sanitizedReasons: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface PolicySubject {
|
|
41
|
+
id: string;
|
|
42
|
+
subjectType: "human" | "runtime" | "system" | "tool";
|
|
43
|
+
tenantId?: string;
|
|
44
|
+
workspaceId?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PolicyResource {
|
|
47
|
+
resourceType: "command" | "evidence" | "file" | "prompt" | "pythonWorker" | "url";
|
|
48
|
+
summary: string;
|
|
49
|
+
tenantId?: string;
|
|
50
|
+
workspaceId?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface PolicyRequest {
|
|
53
|
+
requestId: string;
|
|
54
|
+
subject: PolicySubject;
|
|
55
|
+
action: PolicyAction;
|
|
56
|
+
resource: PolicyResource;
|
|
57
|
+
sink: PolicySink;
|
|
58
|
+
dataClassification: DataClassification;
|
|
59
|
+
segments: PromptSegment[];
|
|
60
|
+
redactionReport: RedactionReport;
|
|
61
|
+
}
|
|
62
|
+
export interface PolicyRequestInput {
|
|
63
|
+
requestId?: string;
|
|
64
|
+
subject?: Partial<PolicySubject>;
|
|
65
|
+
action?: PolicyAction;
|
|
66
|
+
resource?: Partial<PolicyResource>;
|
|
67
|
+
sink?: PolicySink;
|
|
68
|
+
dataClassification?: DataClassification;
|
|
69
|
+
segments?: PromptSegment[];
|
|
70
|
+
redactionReport?: RedactionReport;
|
|
71
|
+
}
|
|
72
|
+
export interface PolicyDecision {
|
|
73
|
+
requestId: string;
|
|
74
|
+
outcome: PolicyDecisionOutcome;
|
|
75
|
+
matchedRuleIds: string[];
|
|
76
|
+
redactionStatus: RedactionStatus;
|
|
77
|
+
sanitizedReasons: string[];
|
|
78
|
+
evidenceSummary: string;
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-types.js","sourceRoot":"","sources":["../../src/security/policy-types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO;IACP,MAAM;IACN,kBAAkB;IAClB,YAAY;CACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PolicySink, PromptSegment, SegmentKind } from "./policy-types.js";
|
|
2
|
+
export interface PromptSegmentInput {
|
|
3
|
+
id?: string;
|
|
4
|
+
kind?: SegmentKind;
|
|
5
|
+
provenance?: string;
|
|
6
|
+
sink?: PolicySink;
|
|
7
|
+
text?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PromptPacketInput {
|
|
10
|
+
segments?: PromptSegmentInput[];
|
|
11
|
+
}
|
|
12
|
+
export declare function intakePromptPacket(packet: PromptPacketInput): PromptSegment[];
|
|
13
|
+
export declare function intakePromptSegment(input: PromptSegmentInput, fallbackId?: string): PromptSegment;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { classifyContent } from "./content-classifier.js";
|
|
3
|
+
export function intakePromptPacket(packet) {
|
|
4
|
+
if (!Array.isArray(packet.segments)) {
|
|
5
|
+
return [unknownSegment("segment-1", "malformed prompt packet")];
|
|
6
|
+
}
|
|
7
|
+
return packet.segments.map((segment, index) => intakePromptSegment(segment, `segment-${index + 1}`));
|
|
8
|
+
}
|
|
9
|
+
export function intakePromptSegment(input, fallbackId = "segment-1") {
|
|
10
|
+
const text = typeof input.text === "string" ? input.text : "";
|
|
11
|
+
const kind = input.kind ?? "unknown";
|
|
12
|
+
return {
|
|
13
|
+
id: input.id ?? fallbackId,
|
|
14
|
+
kind,
|
|
15
|
+
provenance: input.provenance ?? "unknown",
|
|
16
|
+
sink: input.sink ?? "provider",
|
|
17
|
+
text,
|
|
18
|
+
byteLength: Buffer.byteLength(text, "utf8"),
|
|
19
|
+
classification: classifyContent(text),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function unknownSegment(id, text) {
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
kind: "unknown",
|
|
26
|
+
provenance: "unknown",
|
|
27
|
+
sink: "provider",
|
|
28
|
+
text,
|
|
29
|
+
byteLength: Buffer.byteLength(text, "utf8"),
|
|
30
|
+
classification: classifyContent(text),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=prompt-intake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-intake.js","sourceRoot":"","sources":["../../src/security/prompt-intake.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAe1D,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,cAAc,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAC5C,mBAAmB,CAAC,OAAO,EAAE,WAAW,KAAK,GAAG,CAAC,EAAE,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAAyB,EACzB,UAAU,GAAG,WAAW;IAExB,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,SAAS,CAAC;IACrC,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,UAAU;QAC1B,IAAI;QACJ,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,SAAS;QACzC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,UAAU;QAC9B,IAAI;QACJ,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;QAC3C,cAAc,EAAE,eAAe,CAAC,IAAI,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,EAAU,EAAE,IAAY;IAC9C,OAAO;QACL,EAAE;QACF,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,UAAU;QAChB,IAAI;QACJ,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;QAC3C,cAAc,EAAE,eAAe,CAAC,IAAI,CAAC;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const secretReplacement = "[REDACTED_SECRET]";
|
|
2
|
+
const bearerPattern = /\bbearer\s+[a-z0-9._-]{12,}/gi;
|
|
3
|
+
const assignmentPattern = /\b(api[_-]?key|password|secret|token)(\s*[:=]\s*)[^\s"']{12,}/gi;
|
|
4
|
+
const secretPatterns = [bearerPattern, assignmentPattern];
|
|
5
|
+
export function redactPromptSegments(segments) {
|
|
6
|
+
const redactedSegments = segments.map(redactPromptSegment);
|
|
7
|
+
const status = reportStatus(redactedSegments);
|
|
8
|
+
return {
|
|
9
|
+
status,
|
|
10
|
+
redactedSegments,
|
|
11
|
+
sanitizedReasons: sanitizedReasons(redactedSegments, status),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function redactPromptSegment(segment) {
|
|
15
|
+
const secretFindings = segment.classification.findings.filter((finding) => finding.kind === "secretShaped");
|
|
16
|
+
const redactedText = redactSecrets(segment.text);
|
|
17
|
+
const wasRedacted = redactedText !== segment.text;
|
|
18
|
+
const hasRemainingSecret = secretPatterns.some((pattern) => {
|
|
19
|
+
pattern.lastIndex = 0;
|
|
20
|
+
return pattern.test(redactedText);
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
id: segment.id,
|
|
24
|
+
text: redactedText,
|
|
25
|
+
status: redactionStatus(secretFindings.length, wasRedacted, hasRemainingSecret),
|
|
26
|
+
redactedFindings: wasRedacted ? ["secretShaped"] : [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function redactSecrets(text) {
|
|
30
|
+
return text
|
|
31
|
+
.replace(bearerPattern, secretReplacement)
|
|
32
|
+
.replace(assignmentPattern, (_match, label, separator) => {
|
|
33
|
+
return `${label}${separator}${secretReplacement}`;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function redactionStatus(secretFindingCount, wasRedacted, hasRemainingSecret) {
|
|
37
|
+
if (hasRemainingSecret)
|
|
38
|
+
return "unsafeUnredacted";
|
|
39
|
+
if (wasRedacted)
|
|
40
|
+
return "redacted";
|
|
41
|
+
if (secretFindingCount > 0)
|
|
42
|
+
return "unsafeUnredacted";
|
|
43
|
+
return "notRequired";
|
|
44
|
+
}
|
|
45
|
+
function reportStatus(segments) {
|
|
46
|
+
if (segments.some((segment) => segment.status === "unsafeUnredacted")) {
|
|
47
|
+
return "unsafeUnredacted";
|
|
48
|
+
}
|
|
49
|
+
if (segments.some((segment) => segment.status === "redacted")) {
|
|
50
|
+
return "redacted";
|
|
51
|
+
}
|
|
52
|
+
return "notRequired";
|
|
53
|
+
}
|
|
54
|
+
function sanitizedReasons(segments, status) {
|
|
55
|
+
if (status === "notRequired")
|
|
56
|
+
return ["no restricted values detected"];
|
|
57
|
+
return segments
|
|
58
|
+
.filter((segment) => segment.redactedFindings.length > 0)
|
|
59
|
+
.map((segment) => redactionReason(segment.id, segment.redactedFindings));
|
|
60
|
+
}
|
|
61
|
+
function redactionReason(segmentId, findings) {
|
|
62
|
+
return `segment ${segmentId} redacted ${findings.join(", ")}`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function encodeForSink(text, sink) {
|
|
2
|
+
return {
|
|
3
|
+
sink,
|
|
4
|
+
value: sinkEncoder(sink)(text),
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
function sinkEncoder(sink) {
|
|
8
|
+
const encoders = {
|
|
9
|
+
evidence: escapeMarkdown,
|
|
10
|
+
htmlText: escapeHtmlText,
|
|
11
|
+
json: JSON.stringify,
|
|
12
|
+
log: normalizeLogLine,
|
|
13
|
+
markdown: escapeMarkdown,
|
|
14
|
+
provider: wrapProviderData,
|
|
15
|
+
shellArgument: preserveArgumentValue,
|
|
16
|
+
url: encodeURI,
|
|
17
|
+
};
|
|
18
|
+
return encoders[sink];
|
|
19
|
+
}
|
|
20
|
+
function escapeMarkdown(text) {
|
|
21
|
+
return text.replaceAll("```", "`\\`\\`");
|
|
22
|
+
}
|
|
23
|
+
function escapeHtmlText(text) {
|
|
24
|
+
return text
|
|
25
|
+
.replaceAll("&", "&")
|
|
26
|
+
.replaceAll("<", "<")
|
|
27
|
+
.replaceAll(">", ">")
|
|
28
|
+
.replaceAll('"', """)
|
|
29
|
+
.replaceAll("'", "'");
|
|
30
|
+
}
|
|
31
|
+
function normalizeLogLine(text) {
|
|
32
|
+
return text.replaceAll(/\r?\n/g, "\\n");
|
|
33
|
+
}
|
|
34
|
+
function wrapProviderData(text) {
|
|
35
|
+
return `<untrusted-data>${text}</untrusted-data>`;
|
|
36
|
+
}
|
|
37
|
+
function preserveArgumentValue(text) {
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=sink-encoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sink-encoding.js","sourceRoot":"","sources":["../../src/security/sink-encoding.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,IAAgB;IAEhB,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAgB;IACnC,MAAM,QAAQ,GAAG;QACf,QAAQ,EAAE,cAAc;QACxB,QAAQ,EAAE,cAAc;QACxB,IAAI,EAAE,IAAI,CAAC,SAAS;QACpB,GAAG,EAAE,gBAAgB;QACrB,QAAQ,EAAE,cAAc;QACxB,QAAQ,EAAE,gBAAgB;QAC1B,aAAa,EAAE,qBAAqB;QACpC,GAAG,EAAE,SAAS;KAC6C,CAAC;IAC9D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,mBAAmB,IAAI,mBAAmB,CAAC;AACpD,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC;AACd,CAAC"}
|