mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 22:37:29 +00:00
Handle non-finite policy risk scores (#75)
This commit is contained in:
parent
8a86d14ffd
commit
ae371b91d0
2 changed files with 37 additions and 1 deletions
|
|
@ -24,6 +24,12 @@ describe("account-core", () => {
|
||||||
expect(riskBandForScore(score)).toBe("high");
|
expect(riskBandForScore(score)).toBe("high");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("treats non-finite risk scores as critical", () => {
|
||||||
|
expect(riskBandForScore(Number.NaN)).toBe("critical");
|
||||||
|
expect(riskBandForScore(Number.POSITIVE_INFINITY)).toBe("critical");
|
||||||
|
expect(riskBandForScore(Number.NEGATIVE_INFINITY)).toBe("critical");
|
||||||
|
});
|
||||||
|
|
||||||
it("requires approval for gated actions with matching grants", () => {
|
it("requires approval for gated actions with matching grants", () => {
|
||||||
const result = evaluateAccountPolicy({
|
const result = evaluateAccountPolicy({
|
||||||
action: "email:send",
|
action: "email:send",
|
||||||
|
|
@ -41,6 +47,25 @@ describe("account-core", () => {
|
||||||
expect(result.decision).toBe("approval_required");
|
expect(result.decision).toBe("approval_required");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY])("fails closed for non-finite policy risk score %s", (riskScore) => {
|
||||||
|
const result = evaluateAccountPolicy({
|
||||||
|
action: "social:profile:read",
|
||||||
|
riskScore,
|
||||||
|
principal: { type: "agent", id: "reader-agent" },
|
||||||
|
grant: {
|
||||||
|
id: "grant_read",
|
||||||
|
accountId: "account_1",
|
||||||
|
principal: { type: "agent", id: "reader-agent" },
|
||||||
|
permissions: ["social:profile:read"],
|
||||||
|
policy: [],
|
||||||
|
createdAt: new Date(0).toISOString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.riskScore).toBe(1);
|
||||||
|
expect(result.decision).toBe("deny");
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
{ principal: { type: "agent" as const, id: "trusted-agent", trusted: true }, expected: "allow" },
|
{ principal: { type: "agent" as const, id: "trusted-agent", trusted: true }, expected: "allow" },
|
||||||
{ principal: { type: "agent" as const, id: "untrusted-agent", trusted: false }, expected: "approval_required" },
|
{ principal: { type: "agent" as const, id: "untrusted-agent", trusted: false }, expected: "approval_required" },
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ const WRITE_ACTIONS = new Set([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function riskBandForScore(score: number): LogicSrcRiskBand {
|
export function riskBandForScore(score: number): LogicSrcRiskBand {
|
||||||
|
if (!Number.isFinite(score)) {
|
||||||
|
return "critical";
|
||||||
|
}
|
||||||
if (score >= 0.75) {
|
if (score >= 0.75) {
|
||||||
return "critical";
|
return "critical";
|
||||||
}
|
}
|
||||||
|
|
@ -50,8 +53,16 @@ export function scoreAccountActionRisk(input: {
|
||||||
return Math.min(1, Number(score.toFixed(2)));
|
return Math.min(1, Number(score.toFixed(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeRiskScore(score: number): number {
|
||||||
|
if (!Number.isFinite(score)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(1, Math.max(0, score));
|
||||||
|
}
|
||||||
|
|
||||||
export function evaluateAccountPolicy(input: LogicSrcPolicyEvaluationInput): LogicSrcPolicyEvaluationResult {
|
export function evaluateAccountPolicy(input: LogicSrcPolicyEvaluationInput): LogicSrcPolicyEvaluationResult {
|
||||||
const riskScore = Math.min(1, Math.max(0, input.riskScore ?? scoreAccountActionRisk({ action: input.action })));
|
const riskScore = normalizeRiskScore(input.riskScore ?? scoreAccountActionRisk({ action: input.action }));
|
||||||
const grantActive = input.grant && !input.grant.revokedAt && (!input.grant.expiresAt || Date.parse(input.grant.expiresAt) > Date.now());
|
const grantActive = input.grant && !input.grant.revokedAt && (!input.grant.expiresAt || Date.parse(input.grant.expiresAt) > Date.now());
|
||||||
const hasPermission = Boolean(grantActive && input.grant?.permissions.includes(input.action));
|
const hasPermission = Boolean(grantActive && input.grant?.permissions.includes(input.action));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue