fullcourtdefense-cli 1.34.14 → 1.34.15
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.
|
@@ -37,6 +37,13 @@ export interface ResolvedActionPolicyApproval {
|
|
|
37
37
|
onTimeout: ActionPolicyApprovalOnTimeout;
|
|
38
38
|
}
|
|
39
39
|
export interface ActionPolicyRule {
|
|
40
|
+
/** Explicit, narrowly scoped permission to relax named role actions only. */
|
|
41
|
+
roleException?: {
|
|
42
|
+
roleIds: string[];
|
|
43
|
+
verbs: string[];
|
|
44
|
+
};
|
|
45
|
+
/** Set only by the role compiler; never accepted from policy authors. */
|
|
46
|
+
roleVerb?: string;
|
|
40
47
|
operations: string[];
|
|
41
48
|
verdict: ActionPolicyVerdict;
|
|
42
49
|
constraints?: ActionPolicyConstraint[];
|
|
@@ -45,6 +52,7 @@ export interface ActionPolicyRule {
|
|
|
45
52
|
}
|
|
46
53
|
/** Reject ambiguous/broad approval exceptions at authoring time; runtime also fails closed on them. */
|
|
47
54
|
export declare function approvalExceptionError(rule: ActionPolicyRule): string | undefined;
|
|
55
|
+
export declare function roleExceptionError(rule: ActionPolicyRule): string | undefined;
|
|
48
56
|
/**
|
|
49
57
|
* The three kinds of machine a fleet policy can be written for:
|
|
50
58
|
* - `workstation` — laptops / desktops a person uses (default for any machine that does not say otherwise)
|
|
@@ -35,6 +35,7 @@ __export(actionPolicyEngine_exports, {
|
|
|
35
35
|
normalizeMachineKind: () => normalizeMachineKind,
|
|
36
36
|
policyTargetsMachineKind: () => policyTargetsMachineKind,
|
|
37
37
|
resolveApprovalOptions: () => resolveApprovalOptions,
|
|
38
|
+
roleExceptionError: () => roleExceptionError,
|
|
38
39
|
ruleNeedsContentFact: () => ruleNeedsContentFact,
|
|
39
40
|
toolCapabilities: () => toolCapabilities
|
|
40
41
|
});
|
|
@@ -6205,6 +6206,8 @@ function provenPowerShellQueryUrls(command) {
|
|
|
6205
6206
|
}
|
|
6206
6207
|
}
|
|
6207
6208
|
function approvalExceptionError(rule) {
|
|
6209
|
+
const roleError = roleExceptionError(rule);
|
|
6210
|
+
if (roleError) return roleError;
|
|
6208
6211
|
const ids = rule.approval?.replacesPolicyIds;
|
|
6209
6212
|
if (ids === void 0) return void 0;
|
|
6210
6213
|
if (!Array.isArray(ids) || ids.length > 50 || ids.some((id) => typeof id !== "string" || !id.trim())) {
|
|
@@ -6219,6 +6222,19 @@ function approvalExceptionError(rule) {
|
|
|
6219
6222
|
}
|
|
6220
6223
|
return void 0;
|
|
6221
6224
|
}
|
|
6225
|
+
function roleExceptionError(rule) {
|
|
6226
|
+
const exception = rule.roleException;
|
|
6227
|
+
if (exception === void 0) return void 0;
|
|
6228
|
+
if (!exception || rule.verdict !== "allow") return "A role exception must use Allow.";
|
|
6229
|
+
const valid = (values, permitted) => Array.isArray(values) && values.length > 0 && values.length <= permitted.length && values.every((value) => typeof value === "string" && permitted.includes(value));
|
|
6230
|
+
if (!valid(exception.roleIds, ["read_only", "standard", "power", "unrestricted"]) || !valid(exception.verbs, ["read", "write", "delete", "drop", "deploy", "export", "send", "payment"])) {
|
|
6231
|
+
return "Select valid roles and role actions for the exception.";
|
|
6232
|
+
}
|
|
6233
|
+
if (!["agentName", "toolName"].every((field) => rule.constraints?.some((c) => c.field === field && c.operator === "equals" && typeof c.value === "string" && c.value.trim() && !/[?*]/.test(c.value)))) {
|
|
6234
|
+
return "A role exception must identify an exact agent and exact tool.";
|
|
6235
|
+
}
|
|
6236
|
+
return void 0;
|
|
6237
|
+
}
|
|
6222
6238
|
var MACHINE_KINDS = ["workstation", "ci", "workload"];
|
|
6223
6239
|
function normalizeMachineKind(value) {
|
|
6224
6240
|
const kind = (value || "").trim().toLowerCase();
|
|
@@ -7443,9 +7459,13 @@ function evaluateActionPolicies(policies, toolName, operation, context = {}, dec
|
|
|
7443
7459
|
const ruleMatches = (rule) => candidateOperations.some((candidate) => operationMatchesRule(candidate, rule.operations, matchText, toolCaps)) && (!rule.constraints?.length || rule.constraints.every((c) => evaluateConstraint(c, context)));
|
|
7444
7460
|
const replacedApprovalPolicies = /* @__PURE__ */ new Set();
|
|
7445
7461
|
const appliedApprovalExceptions = /* @__PURE__ */ new Set();
|
|
7462
|
+
const roleExceptions = [];
|
|
7463
|
+
const appliedRoleExceptions = /* @__PURE__ */ new Set();
|
|
7464
|
+
let grantingRoleException;
|
|
7446
7465
|
for (const policy of policies) {
|
|
7447
7466
|
if (!policyApplies(policy) || policy.stage === "monitor") continue;
|
|
7448
7467
|
for (const rule of policy.rules) {
|
|
7468
|
+
if (rule.roleException && !roleExceptionError(rule) && ruleMatches(rule)) roleExceptions.push({ policy, rule });
|
|
7449
7469
|
if (rule.verdict !== "require_approval" || rule.approval?.scope !== "developer" || !ruleMatches(rule)) continue;
|
|
7450
7470
|
if (approvalExceptionError(rule) || !Array.isArray(rule.approval.replacesPolicyIds)) continue;
|
|
7451
7471
|
for (const id of rule.approval.replacesPolicyIds) {
|
|
@@ -7463,6 +7483,15 @@ function evaluateActionPolicies(policies, toolName, operation, context = {}, dec
|
|
|
7463
7483
|
const isMonitorStage = policy.stage === "monitor";
|
|
7464
7484
|
for (const rule of policy.rules) {
|
|
7465
7485
|
if (!ruleMatches(rule)) continue;
|
|
7486
|
+
if (!isMonitorStage && policy.resourceType === "machine_role" && policy.enforcement !== "hard" && rule.roleVerb && (rule.verdict === "block" || rule.verdict === "require_approval")) {
|
|
7487
|
+
const roleId = policy.id.startsWith("machine-role:") ? policy.id.split(":")[1] : "";
|
|
7488
|
+
const exception = roleExceptions.find((item) => item.rule.roleException.roleIds.includes(roleId) && item.rule.roleException.verbs.includes(rule.roleVerb));
|
|
7489
|
+
if (exception) {
|
|
7490
|
+
grantingRoleException = exception.policy;
|
|
7491
|
+
appliedRoleExceptions.add(`${exception.policy.name} (${exception.policy.id}) relaxes ${policy.name}: ${rule.roleVerb}`);
|
|
7492
|
+
continue;
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7466
7495
|
if (!isMonitorStage && rule.verdict === "require_approval" && rule.approval?.scope !== "developer" && policy.enforcement !== "hard" && replacedApprovalPolicies.has(policy.id)) {
|
|
7467
7496
|
appliedApprovalExceptions.add(`${policy.name} (${policy.id})`);
|
|
7468
7497
|
continue;
|
|
@@ -7540,6 +7569,13 @@ function evaluateActionPolicies(policies, toolName, operation, context = {}, dec
|
|
|
7540
7569
|
if (monitorMatches.length > 0) {
|
|
7541
7570
|
worstResult = { ...worstResult, monitorMatches };
|
|
7542
7571
|
}
|
|
7572
|
+
if (appliedRoleExceptions.size) {
|
|
7573
|
+
worstResult = {
|
|
7574
|
+
...worstResult,
|
|
7575
|
+
...worstResult.verdict === "allow" && grantingRoleException ? { policyId: grantingRoleException.id, policyName: grantingRoleException.name, resourceType: grantingRoleException.resourceType } : {},
|
|
7576
|
+
reason: `${worstResult.reason || worstResult.verdict}; explicit role exception: ${[...appliedRoleExceptions].sort().join("; ")}`
|
|
7577
|
+
};
|
|
7578
|
+
}
|
|
7543
7579
|
if (skippedOrgPolicies.length > 0) {
|
|
7544
7580
|
worstResult = { ...worstResult, skippedOrgPolicies };
|
|
7545
7581
|
}
|
|
@@ -7692,6 +7728,7 @@ function inventoryToolMatchesActionPolicy(toolName, toolActions, policies) {
|
|
|
7692
7728
|
normalizeMachineKind,
|
|
7693
7729
|
policyTargetsMachineKind,
|
|
7694
7730
|
resolveApprovalOptions,
|
|
7731
|
+
roleExceptionError,
|
|
7695
7732
|
ruleNeedsContentFact,
|
|
7696
7733
|
toolCapabilities
|
|
7697
7734
|
});
|
package/dist/version.json
CHANGED