{"record":{"id":"c895b82d39458ff4","repo":"ruvnet/ruflo","slug":"untrusted-approval-issuer","errorCode":null,"errorMessage":"untrusted-approval-issuer","messagePattern":"untrusted-approval-issuer","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/security/src/policy/engine.ts","lineNumber":108,"sourceCode":"    if (index >= 0) this.state.rules[index] = structuredClone(rule);\n    else this.state.rules.push(structuredClone(rule));\n  }\n\n  setBudget(limit: BudgetLimit): void {\n    if (limit.periodMs <= 0) throw new Error('invalid-budget-period');\n    if (!Number.isFinite(limit.periodMs)\n      || [limit.maxCostUsd, limit.maxTokens].some((value) => (\n        value !== undefined && (!Number.isFinite(value) || value < 0)\n      ))) throw new Error('invalid-budget-limit');\n    const index = this.state.budgets.findIndex((item) => item.id === limit.id);\n    if (index >= 0) this.state.budgets[index] = structuredClone(limit);\n    else this.state.budgets.push(structuredClone(limit));\n  }\n\n  issueApproval(approval: Omit<PolicyApproval, 'uses' | 'issuedAt'> & { uses?: number; issuedAt?: number }): PolicyApproval {\n    if (approval.issuedBy === approval.principal) throw new Error('self-approval-forbidden');\n    if (this.approvalIssuerVerifier?.(approval.issuedBy) !== true) {\n      throw new Error('untrusted-approval-issuer');\n    }\n    const issuedAt = approval.issuedAt ?? this.now();\n    const record: PolicyApproval = { ...approval, issuedAt, uses: approval.uses ?? 0 };\n    if (this.state.approvals.some((item) => item.id === record.id)) throw new Error('duplicate-approval-id');\n    if (!record.id\n      || record.expiresAt <= issuedAt\n      || !Number.isInteger(record.maxUses)\n      || record.maxUses <= 0\n      || !Number.isInteger(record.uses)\n      || record.uses < 0\n      || record.uses > record.maxUses) throw new Error('invalid-approval');\n    this.state.approvals.push(record);\n    return structuredClone(record);\n  }\n\n  revokeApproval(id: string): boolean {\n    const approval = this.state.approvals.find((item) => item.id === id);\n    if (!approval || approval.revokedAt) return false;","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/security/src/policy/engine.ts#L90-L126","documentation":"When the engine was constructed with an approvalIssuerVerifier, every issueApproval() call must have that verifier return exactly true for approval.issuedBy; anything else (false, undefined, a truthy non-boolean) throws Error('untrusted-approval-issuer'). This gates approvals to a known set of issuers.","triggerScenarios":"A new approver id that was never added to the verifier's allowlist; a verifier implemented to return a truthy string or Set.has result coerced oddly; verifier returning undefined because of a missing return path.","commonSituations":"Rotating approver identities without updating the verifier; the verifier closure checking against a stale config snapshot; unit tests constructing an engine with a verifier that allows nothing.","solutions":["Add the issuer's id to whatever allowlist the verifier checks, then retry issuance.","Make the verifier return an explicit boolean (e.g. trustedIssuers.has(id) is fine, but ensure trustedIssuers is the live set).","Only construct the engine without a verifier when you truly accept unauthenticated approvals — in production you should not."],"exampleFix":"// before\nconst verifier = (id: string) => id === 'old-approver' as unknown as boolean;\nengine.issueApproval({ id, issuedBy: 'new-approver', ... }); // throws\n\n// after\nconst trusted = new Set(['old-approver', 'new-approver']);\nconst engine = new PolicyEngine({ approvalIssuerVerifier: (id) => trusted.has(id) });\nengine.issueApproval({ id, issuedBy: 'new-approver', ... });","handlingStrategy":"validation","validationCode":"const trustedIssuers = new Set(['human-ops', 'supervisor']);\nif (!trustedIssuers.has(approval.issuedBy)) {\n  throw new Error(`issuer ${approval.issuedBy} is not in the trusted set`);\n}\n// engine constructed with: new PolicyEngine({ approvalIssuerVerifier: (id) => trustedIssuers.has(id) })","typeGuard":null,"tryCatchPattern":"try {\n  return engine.issueApproval(approval);\n} catch (err) {\n  if (err instanceof Error && err.message === 'untrusted-approval-issuer') {\n    return forbidden(`issuer ${approval.issuedBy} not trusted`);\n  }\n  throw err;\n}","preventionTips":["Keep the verifier's allowlist in live config so rotations take effect without redeploys.","Ensure the verifier returns an explicit boolean (=== true is required, not truthy).","Log issuer ids on rejection to catch stale allowlists quickly."],"tags":["policy","authorization","approval","security"],"backgroundTag":"untrusted-issuer","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}