MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / evaluate

Method evaluate

src/security/permissions.ts:36–85  ·  view source on GitHub ↗

* Returns a non-asking decision based purely on policy. * Returns 'ask' when policy is undecided.

(req: PermissionRequest)

Source from the content-addressed store, hash-verified

34 this.denyPatterns = config.security.autoReject.map(p => new RegExp(p));
35 this.alwaysAskPatterns = (config.security.alwaysAsk ?? []).map(p => new RegExp(p));
36 this.denyRules = [...(config.security.denyRules ?? [])];
37 }
38
39 /**
40 * Returns a non-asking decision based purely on policy.
41 * Returns 'ask' when policy is undecided.
42 */
43 evaluate(req: PermissionRequest): PermissionDecision {
44 // User deny rules outrank everything, including /auto and yolo — that is the point of
45 // being able to write one.
46 if (this.denyRules.length && matchDenyRule(req.operation, this.denyRules)) return 'deny';
47
48 // Hard deny patterns next — even auto-approve mode can't bypass these.
49 for (const p of this.denyPatterns) {
50 if (p.test(req.operation)) return 'deny';
51 }
52
53 // Irreversible commands are confirmed EVERY time. No standing grant, no session
54 // auto-approve, no yolo: rollback cannot undo `rm -rf` or a force push, so a blanket
55 // yes must never reach one. Read-only tools are exempt (their operand is a path).
56 const irreversible =
57 !this.isReadOnlyTool(req.tool) && assessCommand(req.operation).tier === 'irreversible';
58 if (irreversible) {
59 const k = `${req.tool}:${req.operation}`;
60 if (this.sessionDenies.has(k)) return 'deny';
61 return 'ask';
62 }
63
64 // Always-ask patterns next — system-mutating commands (defaults write, sudo,
65 // package installs, disk/network config). These OVERRIDE session auto-approve
66 // and autoApprove patterns: silently running them risks destabilizing the
67 // user's machine, so we force an explicit prompt every time. The only escape
68 // is a per-pair/pattern decision the user themselves granted THIS session
69 // (handled below), so a user who already said "always allow" for one specific
70 // command isn't re-nagged — but `/auto on` alone never covers these.
71 const isAlwaysAsk = this.alwaysAskPatterns.some(p => p.test(req.operation));
72 if (isAlwaysAsk) {
73 const key = `${req.tool}:${req.operation}`;
74 if (this.sessionDenies.has(key)) return 'deny';
75 if (this.sessionAllows.has(key)) return 'allow';
76 if (this.commandGrants.has(normalizeCommand(req.operation))) return 'allow';
77 if (this.alwaysAllowPatterns.some(p => p.test(req.operation))) return 'allow';
78 return 'ask';
79 }
80
81 // Session-wide auto-approve overrides everything except hard denies and
82 // always-ask (handled above).
83 // Set by `/auto on`; cleared by `/auto off`.
84 if (_autoApproveSession) return 'allow';
85
86 // "Allow this tool for the whole session" — from gradient picker
87 if (this.sessionToolAllows.has(req.tool)) return 'allow';
88

Callers 8

executeMethod · 0.80
executeMethod · 0.80
executeMethod · 0.80
executeMethod · 0.80
executeMethod · 0.80
executeMethod · 0.80
executeMethod · 0.80

Calls 2

isReadOnlyToolMethod · 0.95
hasMethod · 0.45

Tested by

no test coverage detected