@amaster.ai/pi-security 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 +122 -0
- package/dist/extension.d.ts +32 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +281 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +517 -0
- package/dist/index.js.map +1 -0
- package/dist/policy-loader.d.ts +4 -0
- package/dist/policy-loader.d.ts.map +1 -0
- package/dist/policy-loader.js +36 -0
- package/dist/policy-loader.js.map +1 -0
- package/examples/auto-review.settings.json +24 -0
- package/examples/reviewer.json +19 -0
- package/package.json +67 -0
- package/preview.png +0 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { JsonObject, RuntimeRequestContext, ToolCallRequest, ToolSource } from '@amaster.ai/pi-shared';
|
|
2
|
+
export type SecurityDecision = {
|
|
3
|
+
kind: 'allow';
|
|
4
|
+
reason?: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'deny';
|
|
7
|
+
reason: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'ask';
|
|
10
|
+
reason: string;
|
|
11
|
+
prompt?: string;
|
|
12
|
+
};
|
|
13
|
+
export type CapabilityPolicy = {
|
|
14
|
+
allow?: string[];
|
|
15
|
+
deny?: string[];
|
|
16
|
+
};
|
|
17
|
+
export type SandboxMode = 'read-only' | 'workspace-write' | 'full-access';
|
|
18
|
+
export type ApprovalMode = 'never' | 'on-failure' | 'on-request' | 'untrusted';
|
|
19
|
+
export type SecurityResourceKind = 'file' | 'shell' | 'network';
|
|
20
|
+
export type SecurityOperation = 'read' | 'write' | 'execute' | 'delete' | 'connect' | 'search';
|
|
21
|
+
export type SecurityScope = 'workspace' | 'home' | 'system' | 'external' | 'unknown';
|
|
22
|
+
export type SecuritySensitivity = 'normal' | 'source' | 'config' | 'secret' | 'credential';
|
|
23
|
+
export type RiskLevel = 'low' | 'medium' | 'high' | 'critical';
|
|
24
|
+
export type SecurityResource = {
|
|
25
|
+
kind: SecurityResourceKind;
|
|
26
|
+
operation: SecurityOperation;
|
|
27
|
+
target?: string;
|
|
28
|
+
scope: SecurityScope;
|
|
29
|
+
sensitivity: SecuritySensitivity;
|
|
30
|
+
};
|
|
31
|
+
export type RiskAssessment = {
|
|
32
|
+
level: RiskLevel;
|
|
33
|
+
reasons: string[];
|
|
34
|
+
};
|
|
35
|
+
export type SecurityRule = {
|
|
36
|
+
id: string;
|
|
37
|
+
priority?: number;
|
|
38
|
+
tools?: string[];
|
|
39
|
+
sources?: ToolSource[];
|
|
40
|
+
triggers?: Array<RuntimeRequestContext['trigger']>;
|
|
41
|
+
senderTrusts?: Array<RuntimeRequestContext['senderTrust']>;
|
|
42
|
+
args?: Record<string, string>;
|
|
43
|
+
argsRegex?: Record<string, string>;
|
|
44
|
+
resources?: SecurityResourceKind[];
|
|
45
|
+
operations?: SecurityOperation[];
|
|
46
|
+
scopes?: SecurityScope[];
|
|
47
|
+
sensitivity?: SecuritySensitivity[];
|
|
48
|
+
risk?: RiskLevel[];
|
|
49
|
+
decision: SecurityDecision;
|
|
50
|
+
};
|
|
51
|
+
export type SecurityPolicyEngineOptions = {
|
|
52
|
+
rules?: SecurityRule[];
|
|
53
|
+
defaultDecision?: SecurityDecision;
|
|
54
|
+
};
|
|
55
|
+
export type SecurityProfileConfig = {
|
|
56
|
+
extends?: string;
|
|
57
|
+
sandbox?: SandboxMode;
|
|
58
|
+
approval?: ApprovalMode;
|
|
59
|
+
rules?: SecurityRule[];
|
|
60
|
+
defaultDecision?: SecurityDecision;
|
|
61
|
+
};
|
|
62
|
+
export type SecurityConfig = {
|
|
63
|
+
defaultProfile?: string;
|
|
64
|
+
profiles?: Record<string, SecurityProfileConfig>;
|
|
65
|
+
};
|
|
66
|
+
export type SecurityEvaluationContext = {
|
|
67
|
+
request: RuntimeRequestContext;
|
|
68
|
+
toolCall: ToolCallRequest;
|
|
69
|
+
workspaceDir?: string;
|
|
70
|
+
resources: SecurityResource[];
|
|
71
|
+
risk: RiskAssessment;
|
|
72
|
+
};
|
|
73
|
+
export type SecurityEvaluationResult = {
|
|
74
|
+
evaluationId: string;
|
|
75
|
+
decision: SecurityDecision;
|
|
76
|
+
resources: SecurityResource[];
|
|
77
|
+
risk: RiskAssessment;
|
|
78
|
+
matchedRuleIds: string[];
|
|
79
|
+
};
|
|
80
|
+
export type SecurityAuditEvent = SecurityEvaluationResult & {
|
|
81
|
+
sessionId: string;
|
|
82
|
+
conversationId: string;
|
|
83
|
+
traceId?: string;
|
|
84
|
+
toolCallId: string;
|
|
85
|
+
toolName: string;
|
|
86
|
+
createdAt: string;
|
|
87
|
+
approvalId?: string;
|
|
88
|
+
};
|
|
89
|
+
export type SecurityApprovalRequest = {
|
|
90
|
+
request: RuntimeRequestContext;
|
|
91
|
+
toolCall: ToolCallRequest;
|
|
92
|
+
decision: Extract<SecurityDecision, {
|
|
93
|
+
kind: 'ask';
|
|
94
|
+
}>;
|
|
95
|
+
evaluation: SecurityEvaluationResult;
|
|
96
|
+
};
|
|
97
|
+
export type SecurityApprovalHandler = (input: SecurityApprovalRequest) => Promise<SecurityDecision>;
|
|
98
|
+
export type SecurityAuditSink = (event: SecurityAuditEvent) => void | Promise<void>;
|
|
99
|
+
export type SecurityGateAuthorizeInput = {
|
|
100
|
+
request: RuntimeRequestContext;
|
|
101
|
+
toolCall: ToolCallRequest;
|
|
102
|
+
workspaceDir?: string;
|
|
103
|
+
};
|
|
104
|
+
export type SecurityGateOptions = {
|
|
105
|
+
profile: string;
|
|
106
|
+
config?: SecurityConfig;
|
|
107
|
+
filePolicies?: Record<string, SecurityProfileConfig>;
|
|
108
|
+
engine?: SecurityPolicyEngine;
|
|
109
|
+
approvalHandler?: SecurityApprovalHandler;
|
|
110
|
+
auditSink?: SecurityAuditSink;
|
|
111
|
+
};
|
|
112
|
+
export declare class SecurityPolicyEngine {
|
|
113
|
+
private readonly entries;
|
|
114
|
+
private readonly defaultDecision;
|
|
115
|
+
constructor(options?: SecurityPolicyEngineOptions);
|
|
116
|
+
decide(input: {
|
|
117
|
+
request: RuntimeRequestContext;
|
|
118
|
+
toolCall: ToolCallRequest;
|
|
119
|
+
workspaceDir?: string;
|
|
120
|
+
}): SecurityDecision;
|
|
121
|
+
evaluate(input: {
|
|
122
|
+
request: RuntimeRequestContext;
|
|
123
|
+
toolCall: ToolCallRequest;
|
|
124
|
+
workspaceDir?: string;
|
|
125
|
+
}): SecurityEvaluationResult;
|
|
126
|
+
}
|
|
127
|
+
export declare class SecurityGate {
|
|
128
|
+
private readonly engine;
|
|
129
|
+
private readonly approvalHandler;
|
|
130
|
+
private readonly auditSink;
|
|
131
|
+
constructor(options: SecurityGateOptions);
|
|
132
|
+
authorize(input: SecurityGateAuthorizeInput): Promise<SecurityEvaluationResult>;
|
|
133
|
+
private resolveDecision;
|
|
134
|
+
private audit;
|
|
135
|
+
}
|
|
136
|
+
export declare function createSecurityGate(options: SecurityGateOptions): SecurityGate;
|
|
137
|
+
export declare function assertSecurityAllowed(input: {
|
|
138
|
+
request: RuntimeRequestContext;
|
|
139
|
+
securityGate: SecurityGate;
|
|
140
|
+
toolCall: ToolCallRequest;
|
|
141
|
+
workspaceDir?: string;
|
|
142
|
+
}): Promise<SecurityEvaluationResult>;
|
|
143
|
+
export declare function assertSecurityDecisionAllowed(decision: SecurityDecision): void;
|
|
144
|
+
export declare function resolveCapabilityPolicy(profile: string, config?: SecurityConfig, filePolicies?: Record<string, SecurityProfileConfig>): CapabilityPolicy;
|
|
145
|
+
export declare function isCapabilityExposed(toolName: string, policy: CapabilityPolicy): boolean;
|
|
146
|
+
export declare function createSecurityPolicyEngineForProfile(profile: string, config?: SecurityConfig, filePolicies?: Record<string, SecurityProfileConfig>): SecurityPolicyEngine;
|
|
147
|
+
export declare function classifySecurityResources(input: {
|
|
148
|
+
request: RuntimeRequestContext;
|
|
149
|
+
toolCall: ToolCallRequest;
|
|
150
|
+
workspaceDir?: string;
|
|
151
|
+
}): SecurityResource[];
|
|
152
|
+
export declare function assessRisk(input: {
|
|
153
|
+
toolCall: ToolCallRequest;
|
|
154
|
+
resources: SecurityResource[];
|
|
155
|
+
}): RiskAssessment;
|
|
156
|
+
export declare function securityEvaluationDetails(evaluation: SecurityEvaluationResult): JsonObject;
|
|
157
|
+
export { default } from './extension.js';
|
|
158
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const DEFAULT_DECISION = {
|
|
4
|
+
kind: 'ask',
|
|
5
|
+
reason: 'No security rule matched this tool call.',
|
|
6
|
+
};
|
|
7
|
+
const SANDBOX_CAPABILITIES = {
|
|
8
|
+
'read-only': {
|
|
9
|
+
allow: ['read', 'ls', 'find', 'grep'],
|
|
10
|
+
deny: ['write', 'edit', 'bash'],
|
|
11
|
+
},
|
|
12
|
+
'workspace-write': {
|
|
13
|
+
allow: ['read', 'ls', 'find', 'grep', 'write', 'edit', 'bash'],
|
|
14
|
+
deny: [],
|
|
15
|
+
},
|
|
16
|
+
'full-access': { allow: ['*'] },
|
|
17
|
+
};
|
|
18
|
+
const BUILTIN_PROFILES = {
|
|
19
|
+
'read-only': { sandbox: 'read-only', approval: 'never' },
|
|
20
|
+
default: { sandbox: 'workspace-write', approval: 'on-request' },
|
|
21
|
+
auto: { sandbox: 'workspace-write', approval: 'on-failure' },
|
|
22
|
+
'full-access': { sandbox: 'full-access', approval: 'never' },
|
|
23
|
+
};
|
|
24
|
+
const DEFAULT_BUILTIN_PROFILE = BUILTIN_PROFILES.default;
|
|
25
|
+
const DENY_SECRETS_RULE = {
|
|
26
|
+
id: 'deny-secrets',
|
|
27
|
+
priority: 600,
|
|
28
|
+
sensitivity: ['secret', 'credential'],
|
|
29
|
+
decision: { kind: 'deny', reason: 'Secret or credential resources are protected.' },
|
|
30
|
+
};
|
|
31
|
+
const BASELINE_SECURITY_RULES = [DENY_SECRETS_RULE];
|
|
32
|
+
const ASK_WORKSPACE_WRITE_RULE = {
|
|
33
|
+
id: 'ask-workspace-write',
|
|
34
|
+
priority: 260,
|
|
35
|
+
resources: ['file'],
|
|
36
|
+
operations: ['write', 'delete'],
|
|
37
|
+
scopes: ['workspace'],
|
|
38
|
+
decision: {
|
|
39
|
+
kind: 'ask',
|
|
40
|
+
reason: 'Workspace file modifications require approval.',
|
|
41
|
+
prompt: 'Approve this file modification?',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
const ASK_HIGH_RISK_RULES = [
|
|
45
|
+
{
|
|
46
|
+
id: 'ask-critical-risk',
|
|
47
|
+
priority: 500,
|
|
48
|
+
risk: ['critical'],
|
|
49
|
+
decision: { kind: 'ask', reason: 'Critical risk operation requires approval.' },
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'ask-high-risk',
|
|
53
|
+
priority: 420,
|
|
54
|
+
risk: ['high'],
|
|
55
|
+
decision: { kind: 'ask', reason: 'High risk operation requires approval.' },
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
function builtinRulesForApproval(approval) {
|
|
59
|
+
switch (approval) {
|
|
60
|
+
case 'never':
|
|
61
|
+
return [...BASELINE_SECURITY_RULES];
|
|
62
|
+
case 'on-failure':
|
|
63
|
+
return [...BASELINE_SECURITY_RULES, ...ASK_HIGH_RISK_RULES];
|
|
64
|
+
case 'on-request':
|
|
65
|
+
return [...BASELINE_SECURITY_RULES, ASK_WORKSPACE_WRITE_RULE];
|
|
66
|
+
case 'untrusted':
|
|
67
|
+
return [...BASELINE_SECURITY_RULES, ...ASK_HIGH_RISK_RULES, ASK_WORKSPACE_WRITE_RULE];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function defaultDecisionForApproval(approval) {
|
|
71
|
+
if (approval === 'untrusted') {
|
|
72
|
+
return { kind: 'ask', reason: 'Untrusted profile requires approval for unknown tool calls.' };
|
|
73
|
+
}
|
|
74
|
+
return { kind: 'allow' };
|
|
75
|
+
}
|
|
76
|
+
export class SecurityPolicyEngine {
|
|
77
|
+
entries;
|
|
78
|
+
defaultDecision;
|
|
79
|
+
constructor(options = {}) {
|
|
80
|
+
this.entries = [...(options.rules ?? [])].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
81
|
+
this.defaultDecision = options.defaultDecision ?? DEFAULT_DECISION;
|
|
82
|
+
}
|
|
83
|
+
decide(input) {
|
|
84
|
+
return this.evaluate(input).decision;
|
|
85
|
+
}
|
|
86
|
+
evaluate(input) {
|
|
87
|
+
const resources = classifySecurityResources(input);
|
|
88
|
+
const risk = assessRisk({ ...input, resources });
|
|
89
|
+
const context = { ...input, resources, risk };
|
|
90
|
+
const matchedRuleIds = [];
|
|
91
|
+
for (const entry of this.entries) {
|
|
92
|
+
if (matchesSecurityRule(entry, context)) {
|
|
93
|
+
matchedRuleIds.push(entry.id);
|
|
94
|
+
return {
|
|
95
|
+
evaluationId: randomUUID(),
|
|
96
|
+
decision: normalizeForInteraction(entry.decision, input.request.interactive),
|
|
97
|
+
resources,
|
|
98
|
+
risk,
|
|
99
|
+
matchedRuleIds,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
evaluationId: randomUUID(),
|
|
105
|
+
decision: normalizeForInteraction(this.defaultDecision, input.request.interactive),
|
|
106
|
+
resources,
|
|
107
|
+
risk,
|
|
108
|
+
matchedRuleIds,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export class SecurityGate {
|
|
113
|
+
engine;
|
|
114
|
+
approvalHandler;
|
|
115
|
+
auditSink;
|
|
116
|
+
constructor(options) {
|
|
117
|
+
this.engine =
|
|
118
|
+
options.engine ??
|
|
119
|
+
createSecurityPolicyEngineForProfile(options.profile, options.config, options.filePolicies);
|
|
120
|
+
this.approvalHandler = options.approvalHandler;
|
|
121
|
+
this.auditSink = options.auditSink;
|
|
122
|
+
}
|
|
123
|
+
async authorize(input) {
|
|
124
|
+
const initial = this.engine.evaluate(input);
|
|
125
|
+
const decision = await this.resolveDecision(input, initial);
|
|
126
|
+
const evaluation = { ...initial, decision };
|
|
127
|
+
await this.audit(input, evaluation);
|
|
128
|
+
return evaluation;
|
|
129
|
+
}
|
|
130
|
+
async resolveDecision(input, evaluation) {
|
|
131
|
+
if (evaluation.decision.kind !== 'ask') {
|
|
132
|
+
return evaluation.decision;
|
|
133
|
+
}
|
|
134
|
+
if (!this.approvalHandler) {
|
|
135
|
+
return evaluation.decision;
|
|
136
|
+
}
|
|
137
|
+
const resolved = await this.approvalHandler({
|
|
138
|
+
request: input.request,
|
|
139
|
+
toolCall: input.toolCall,
|
|
140
|
+
decision: evaluation.decision,
|
|
141
|
+
evaluation,
|
|
142
|
+
});
|
|
143
|
+
return resolved;
|
|
144
|
+
}
|
|
145
|
+
async audit(input, evaluation) {
|
|
146
|
+
if (!this.auditSink) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await this.auditSink({
|
|
150
|
+
...evaluation,
|
|
151
|
+
sessionId: input.request.sessionId,
|
|
152
|
+
conversationId: input.request.conversationId,
|
|
153
|
+
...(input.request.traceId ? { traceId: input.request.traceId } : {}),
|
|
154
|
+
toolCallId: input.toolCall.id,
|
|
155
|
+
toolName: input.toolCall.name,
|
|
156
|
+
createdAt: new Date().toISOString(),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function createSecurityGate(options) {
|
|
161
|
+
return new SecurityGate(options);
|
|
162
|
+
}
|
|
163
|
+
export async function assertSecurityAllowed(input) {
|
|
164
|
+
const evaluation = await input.securityGate.authorize({
|
|
165
|
+
request: input.request,
|
|
166
|
+
toolCall: input.toolCall,
|
|
167
|
+
...(input.workspaceDir ? { workspaceDir: input.workspaceDir } : {}),
|
|
168
|
+
});
|
|
169
|
+
assertSecurityDecisionAllowed(evaluation.decision);
|
|
170
|
+
return evaluation;
|
|
171
|
+
}
|
|
172
|
+
export function assertSecurityDecisionAllowed(decision) {
|
|
173
|
+
switch (decision.kind) {
|
|
174
|
+
case 'allow':
|
|
175
|
+
return;
|
|
176
|
+
case 'ask':
|
|
177
|
+
throw new Error(`Tool call requires approval: ${decision.reason}`);
|
|
178
|
+
case 'deny':
|
|
179
|
+
throw new Error(`Tool call denied by security policy: ${decision.reason}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function resolveCapabilityPolicy(profile, config = {}, filePolicies = {}) {
|
|
183
|
+
return resolveSecurityProfile(profile, config, [], filePolicies).capabilities;
|
|
184
|
+
}
|
|
185
|
+
export function isCapabilityExposed(toolName, policy) {
|
|
186
|
+
const normalized = normalizePatternValue(toolName);
|
|
187
|
+
if (policy.deny?.some((entry) => matchesPattern(normalized, entry))) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (!policy.allow?.length) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return policy.allow.some((entry) => matchesPattern(normalized, entry));
|
|
194
|
+
}
|
|
195
|
+
export function createSecurityPolicyEngineForProfile(profile, config = {}, filePolicies = {}) {
|
|
196
|
+
const resolvedProfile = resolveSecurityProfile(profile, config, [], filePolicies);
|
|
197
|
+
const denyCapabilityRules = resolvedProfile.capabilities.deny?.map((toolName) => ({
|
|
198
|
+
id: `deny-capability-${profile}-${toolName}`,
|
|
199
|
+
priority: 110,
|
|
200
|
+
tools: [toolName],
|
|
201
|
+
decision: {
|
|
202
|
+
kind: 'deny',
|
|
203
|
+
reason: `Tool '${toolName}' is denied by sandbox '${resolvedProfile.sandbox}'.`,
|
|
204
|
+
},
|
|
205
|
+
})) ?? [];
|
|
206
|
+
const allowCapabilityRules = resolvedProfile.capabilities.allow?.map((toolName) => ({
|
|
207
|
+
id: `allow-capability-${profile}-${toolName}`,
|
|
208
|
+
priority: 100,
|
|
209
|
+
tools: [toolName],
|
|
210
|
+
decision: { kind: 'allow' },
|
|
211
|
+
})) ?? [];
|
|
212
|
+
return new SecurityPolicyEngine({
|
|
213
|
+
rules: resolvedProfile.rules.concat(denyCapabilityRules).concat(allowCapabilityRules),
|
|
214
|
+
defaultDecision: resolvedProfile.defaultDecision ?? defaultDecisionForApproval(resolvedProfile.approval),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
export function classifySecurityResources(input) {
|
|
218
|
+
const { toolCall } = input;
|
|
219
|
+
if (toolCall.name === 'read') {
|
|
220
|
+
return [fileResource('read', stringArg(toolCall.args, 'path'), input.workspaceDir)];
|
|
221
|
+
}
|
|
222
|
+
if (toolCall.name === 'ls') {
|
|
223
|
+
return [fileResource('read', stringArg(toolCall.args, 'path'), input.workspaceDir)];
|
|
224
|
+
}
|
|
225
|
+
if (toolCall.name === 'find' || toolCall.name === 'grep') {
|
|
226
|
+
return [
|
|
227
|
+
fileResource('search', stringArg(toolCall.args, 'path') ?? stringArg(toolCall.args, 'cwd'), input.workspaceDir),
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
if (toolCall.name === 'write' || toolCall.name === 'edit') {
|
|
231
|
+
return [fileResource('write', stringArg(toolCall.args, 'path'), input.workspaceDir)];
|
|
232
|
+
}
|
|
233
|
+
if (toolCall.name === 'bash') {
|
|
234
|
+
const command = stringArg(toolCall.args, 'command') ?? toolCall.name;
|
|
235
|
+
const resources = [
|
|
236
|
+
{
|
|
237
|
+
kind: 'shell',
|
|
238
|
+
operation: 'execute',
|
|
239
|
+
target: command,
|
|
240
|
+
scope: fileScope(stringArg(toolCall.args, 'cwd'), input.workspaceDir),
|
|
241
|
+
sensitivity: 'normal',
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
if (/https?:\/\/|(^|\s)(curl|wget|ssh|scp|rsync)\b/i.test(command)) {
|
|
245
|
+
resources.push({
|
|
246
|
+
kind: 'network',
|
|
247
|
+
operation: 'connect',
|
|
248
|
+
target: command,
|
|
249
|
+
scope: 'external',
|
|
250
|
+
sensitivity: 'normal',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return resources;
|
|
254
|
+
}
|
|
255
|
+
return [
|
|
256
|
+
{
|
|
257
|
+
kind: 'shell',
|
|
258
|
+
operation: 'execute',
|
|
259
|
+
target: toolCall.name,
|
|
260
|
+
scope: 'unknown',
|
|
261
|
+
sensitivity: 'normal',
|
|
262
|
+
},
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
export function assessRisk(input) {
|
|
266
|
+
const reasons = [];
|
|
267
|
+
let level = 'low';
|
|
268
|
+
const raise = (next, reason) => {
|
|
269
|
+
if (riskRank(next) > riskRank(level)) {
|
|
270
|
+
level = next;
|
|
271
|
+
}
|
|
272
|
+
reasons.push(reason);
|
|
273
|
+
};
|
|
274
|
+
for (const resource of input.resources) {
|
|
275
|
+
if (resource.sensitivity === 'secret' || resource.sensitivity === 'credential') {
|
|
276
|
+
raise('critical', `Sensitive ${resource.sensitivity} resource`);
|
|
277
|
+
}
|
|
278
|
+
if (resource.kind === 'file' && resource.operation === 'write') {
|
|
279
|
+
raise(resource.scope === 'workspace' ? 'medium' : 'high', `File write in ${resource.scope} scope`);
|
|
280
|
+
}
|
|
281
|
+
if (resource.kind === 'shell') {
|
|
282
|
+
raise('medium', 'Shell/code execution');
|
|
283
|
+
const command = resource.target ?? '';
|
|
284
|
+
if (/\brm\s+-rf\b|:\(\)\s*\{|\bmkfs\b|\bdd\s+if=|\bshutdown\b|\breboot\b/i.test(command)) {
|
|
285
|
+
raise('critical', 'Destructive shell command');
|
|
286
|
+
}
|
|
287
|
+
if (/\bsudo\b|\bchmod\b|\bchown\b|\bkill(all)?\b|\blaunchctl\b/i.test(command)) {
|
|
288
|
+
raise('high', 'Privileged or permission-changing shell command');
|
|
289
|
+
}
|
|
290
|
+
if (/\b(npm|pnpm|yarn|pip|brew|apt|apt-get)\s+(install|add|upgrade|update)\b/i.test(command)) {
|
|
291
|
+
raise('high', 'Dependency or package manager mutation');
|
|
292
|
+
}
|
|
293
|
+
if (/curl\b.*\|\s*(sh|bash)|wget\b.*\|\s*(sh|bash)/i.test(command)) {
|
|
294
|
+
raise('critical', 'Remote script execution');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (resource.kind === 'network') {
|
|
298
|
+
raise('high', 'External network access');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
level,
|
|
303
|
+
reasons: reasons.length ? [...new Set(reasons)] : ['Low risk read-only operation'],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
export function securityEvaluationDetails(evaluation) {
|
|
307
|
+
return {
|
|
308
|
+
decision: evaluation.decision.kind,
|
|
309
|
+
risk: evaluation.risk.level,
|
|
310
|
+
riskReasons: evaluation.risk.reasons,
|
|
311
|
+
matchedRules: evaluation.matchedRuleIds,
|
|
312
|
+
resources: evaluation.resources.map((resource) => ({
|
|
313
|
+
kind: resource.kind,
|
|
314
|
+
operation: resource.operation,
|
|
315
|
+
scope: resource.scope,
|
|
316
|
+
sensitivity: resource.sensitivity,
|
|
317
|
+
...(resource.target ? { target: summarizeTarget(resource.target) } : {}),
|
|
318
|
+
})),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function resolvedFromBuiltin(name) {
|
|
322
|
+
const builtin = BUILTIN_PROFILES[name] ?? DEFAULT_BUILTIN_PROFILE;
|
|
323
|
+
return {
|
|
324
|
+
sandbox: builtin.sandbox,
|
|
325
|
+
approval: builtin.approval,
|
|
326
|
+
capabilities: SANDBOX_CAPABILITIES[builtin.sandbox],
|
|
327
|
+
rules: builtinRulesForApproval(builtin.approval),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function resolveSecurityProfile(profile, config, seen = [], filePolicies = {}) {
|
|
331
|
+
const normalizedProfile = profile.trim();
|
|
332
|
+
if (seen.includes(normalizedProfile)) {
|
|
333
|
+
return resolvedFromBuiltin('default');
|
|
334
|
+
}
|
|
335
|
+
const configured = filePolicies[normalizedProfile] ?? config.profiles?.[normalizedProfile];
|
|
336
|
+
const parent = configured?.extends
|
|
337
|
+
? resolveSecurityProfile(configured.extends, config, seen.concat(normalizedProfile), filePolicies)
|
|
338
|
+
: resolvedFromBuiltin(normalizedProfile);
|
|
339
|
+
if (!configured) {
|
|
340
|
+
return parent;
|
|
341
|
+
}
|
|
342
|
+
const sandbox = configured.sandbox ?? parent.sandbox;
|
|
343
|
+
const approval = configured.approval ?? parent.approval;
|
|
344
|
+
const sandboxChanged = configured.sandbox !== undefined && configured.sandbox !== parent.sandbox;
|
|
345
|
+
const approvalChanged = configured.approval !== undefined && configured.approval !== parent.approval;
|
|
346
|
+
const inheritedDefault = configured.defaultDecision ?? parent.defaultDecision;
|
|
347
|
+
return {
|
|
348
|
+
sandbox,
|
|
349
|
+
approval,
|
|
350
|
+
capabilities: sandboxChanged ? SANDBOX_CAPABILITIES[sandbox] : parent.capabilities,
|
|
351
|
+
rules: (approvalChanged ? builtinRulesForApproval(approval) : parent.rules).concat(normalizeConfiguredRules(configured.rules)),
|
|
352
|
+
...(inheritedDefault ? { defaultDecision: inheritedDefault } : {}),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function normalizeConfiguredRules(rules) {
|
|
356
|
+
return (rules ?? []).map((rule) => ({
|
|
357
|
+
...rule,
|
|
358
|
+
priority: rule.priority ?? 300,
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
function matchesSecurityRule(rule, context) {
|
|
362
|
+
if (rule.tools?.length &&
|
|
363
|
+
!rule.tools.some((tool) => matchesPattern(context.toolCall.name, tool))) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
if (rule.sources?.length && !rule.sources.includes(context.toolCall.source)) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
if (rule.triggers?.length && !rule.triggers.includes(context.request.trigger)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
if (rule.senderTrusts?.length && !rule.senderTrusts.includes(context.request.senderTrust)) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
if (rule.args && !matchesArgs(rule.args, context.toolCall.args)) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
if (rule.argsRegex && !matchesArgsRegex(rule.argsRegex, context.toolCall.args)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
if (rule.risk?.length && !rule.risk.includes(context.risk.level)) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
if (hasResourceCriteria(rule) &&
|
|
385
|
+
!context.resources.some((resource) => matchesResourceCriteria(rule, resource))) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
function hasResourceCriteria(rule) {
|
|
391
|
+
return Boolean(rule.resources?.length ||
|
|
392
|
+
rule.operations?.length ||
|
|
393
|
+
rule.scopes?.length ||
|
|
394
|
+
rule.sensitivity?.length);
|
|
395
|
+
}
|
|
396
|
+
function matchesResourceCriteria(rule, resource) {
|
|
397
|
+
if (rule.resources?.length && !rule.resources.includes(resource.kind)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
if (rule.operations?.length && !rule.operations.includes(resource.operation)) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
if (rule.scopes?.length && !rule.scopes.includes(resource.scope)) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
if (rule.sensitivity?.length && !rule.sensitivity.includes(resource.sensitivity)) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
function normalizeForInteraction(decision, interactive) {
|
|
412
|
+
if (decision.kind === 'ask' && !interactive) {
|
|
413
|
+
return {
|
|
414
|
+
kind: 'deny',
|
|
415
|
+
reason: `Security policy requires approval but the context is non-interactive: ${decision.reason}`,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return decision;
|
|
419
|
+
}
|
|
420
|
+
function fileResource(operation, value, workspaceDir) {
|
|
421
|
+
return {
|
|
422
|
+
kind: 'file',
|
|
423
|
+
operation,
|
|
424
|
+
...(value ? { target: value } : {}),
|
|
425
|
+
scope: fileScope(value, workspaceDir),
|
|
426
|
+
sensitivity: classifyPathSensitivity(value),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function fileScope(value, workspaceDir) {
|
|
430
|
+
if (!value) {
|
|
431
|
+
return 'unknown';
|
|
432
|
+
}
|
|
433
|
+
if (!path.isAbsolute(value)) {
|
|
434
|
+
return 'workspace';
|
|
435
|
+
}
|
|
436
|
+
const normalized = path.resolve(value);
|
|
437
|
+
if (workspaceDir && pathInside(normalized, path.resolve(workspaceDir))) {
|
|
438
|
+
return 'workspace';
|
|
439
|
+
}
|
|
440
|
+
const home = process.env.HOME;
|
|
441
|
+
if (home && pathInside(normalized, path.resolve(home))) {
|
|
442
|
+
return 'home';
|
|
443
|
+
}
|
|
444
|
+
return 'system';
|
|
445
|
+
}
|
|
446
|
+
function pathInside(value, parent) {
|
|
447
|
+
const relative = path.relative(parent, value);
|
|
448
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
449
|
+
}
|
|
450
|
+
function classifyPathSensitivity(value) {
|
|
451
|
+
const lower = value?.toLowerCase() ?? '';
|
|
452
|
+
if (!lower) {
|
|
453
|
+
return 'normal';
|
|
454
|
+
}
|
|
455
|
+
if (/(^|\/)\.ssh(\/|$)|id_rsa|id_ed25519|private[_-]?key|credential|password|token|secret|\.pem$|\.key$/i.test(lower)) {
|
|
456
|
+
return 'credential';
|
|
457
|
+
}
|
|
458
|
+
if (/(^|\/)\.env(\.|$)|(^|\/)\.npmrc$|(^|\/)\.pypirc$|(^|\/)secrets?(\/|$)/i.test(lower)) {
|
|
459
|
+
return 'secret';
|
|
460
|
+
}
|
|
461
|
+
if (/(^|\/)(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock|tsconfig\.json|dockerfile|docker-compose\.ya?ml)$/i.test(lower)) {
|
|
462
|
+
return 'config';
|
|
463
|
+
}
|
|
464
|
+
if (/\.(ts|tsx|js|jsx|py|go|rs|java|kt|swift|c|cc|cpp|h|hpp|css|html|md)$/i.test(lower)) {
|
|
465
|
+
return 'source';
|
|
466
|
+
}
|
|
467
|
+
return 'normal';
|
|
468
|
+
}
|
|
469
|
+
function matchesArgs(patterns, args) {
|
|
470
|
+
for (const [key, expected] of Object.entries(patterns)) {
|
|
471
|
+
const actual = args[key];
|
|
472
|
+
if (typeof actual !== 'string' || !matchesPattern(actual, expected)) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
function matchesArgsRegex(patterns, args) {
|
|
479
|
+
for (const [key, pattern] of Object.entries(patterns)) {
|
|
480
|
+
const actual = args[key];
|
|
481
|
+
if (typeof actual !== 'string' || !new RegExp(pattern).test(actual)) {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
function matchesPattern(value, pattern) {
|
|
488
|
+
const normalizedValue = normalizePatternValue(value);
|
|
489
|
+
const normalizedPattern = normalizePatternValue(pattern);
|
|
490
|
+
if (normalizedPattern === '*') {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
if (normalizedPattern.endsWith(':*')) {
|
|
494
|
+
return normalizedValue.startsWith(normalizedPattern.slice(0, -1));
|
|
495
|
+
}
|
|
496
|
+
if (normalizedPattern.endsWith('*')) {
|
|
497
|
+
return normalizedValue.startsWith(normalizedPattern.slice(0, -1));
|
|
498
|
+
}
|
|
499
|
+
return normalizedValue === normalizedPattern;
|
|
500
|
+
}
|
|
501
|
+
function normalizePatternValue(value) {
|
|
502
|
+
return value.trim().toLowerCase();
|
|
503
|
+
}
|
|
504
|
+
function stringArg(args, key) {
|
|
505
|
+
const value = args[key];
|
|
506
|
+
return typeof value === 'string' ? value : undefined;
|
|
507
|
+
}
|
|
508
|
+
function riskRank(level) {
|
|
509
|
+
return { low: 1, medium: 2, high: 3, critical: 4 }[level];
|
|
510
|
+
}
|
|
511
|
+
function summarizeTarget(value) {
|
|
512
|
+
return value.length > 200
|
|
513
|
+
? `${value.slice(0, 200)}...[${value.length - 200} chars truncated]`
|
|
514
|
+
: value;
|
|
515
|
+
}
|
|
516
|
+
export { default } from './extension.js';
|
|
517
|
+
//# sourceMappingURL=index.js.map
|