{"record":{"id":"2acc3d102073380a","repo":"paperclipai/paperclip","slug":"cloud-control-assertion-does-not-authorize-this-action","errorCode":null,"errorMessage":"Cloud control assertion does not authorize this action","messagePattern":"Cloud control assertion does not authorize this action","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":555,"sourceCode":"    || payload.aud !== CLOUD_CONTROL_AUDIENCE\n    || typeof payload.sub !== \"string\"\n    || typeof payload.action !== \"string\"\n    || typeof payload.requestId !== \"string\"\n    || typeof payload.iat !== \"number\"\n    || !Number.isInteger(payload.iat)\n    || typeof payload.exp !== \"number\"\n    || !Number.isInteger(payload.exp)\n  ) {\n    throw new Error(\"Cloud control claims are incomplete\");\n  }\n  if (!configuredStackId || payload.sub !== configuredStackId) {\n    throw new Error(\"Cloud control assertion stack does not match this instance\");\n  }\n  if (\n    !(CLOUD_CONTROL_ACTIONS as readonly string[]).includes(payload.action)\n    || payload.action !== input.expectedAction\n  ) {\n    throw new Error(\"Cloud control assertion does not authorize this action\");\n  }\n  if (\n    !payload.requestId\n    || payload.requestId.trim() !== payload.requestId\n    || payload.requestId.length > 256\n  ) {\n    throw new Error(\"Cloud control assertion request id is invalid\");\n  }\n  if (\n    payload.exp <= nowSeconds\n    || payload.iat > nowSeconds + MAX_CLOCK_SKEW_SECONDS\n    || payload.exp <= payload.iat\n    || payload.exp - payload.iat > CLOUD_CONTROL_MAX_LIFETIME_SECONDS\n  ) {\n    throw new Error(\"Cloud control assertion is expired or has an invalid lifetime\");\n  }\n  // Consumed LAST, only after every other check passed: a rejected\n  // assertion must not burn its request id, or an attacker could deny a","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/cloud-runtime-identity.ts#L537-L573","documentation":"verifyCloudControlAssertion enforces least privilege: the payload's action claim must be a member of the CLOUD_CONTROL_ACTIONS allowlist and must exactly equal the expectedAction the endpoint demands. This error means the assertion is otherwise valid but does not authorize the operation being attempted.","triggerScenarios":"Calling an endpoint expecting action \"restart\" with an assertion minted for \"logs\" or \"status\"; the action claim contains a value not in CLOUD_CONTROL_ACTIONS (e.g. a typo'd or invented action); an assertion reused across different control endpoints with different expectedAction values.","commonSituations":"A control client caches one assertion and reuses it for multiple operations; an action name changed server-side while clients still mint the old name; case/spacing differences (\"Restart\" vs \"restart\"); a new control operation was added but not yet added to CLOUD_CONTROL_ACTIONS.","solutions":["Mint a fresh assertion whose action claim exactly equals the expectedAction of the endpoint you are calling (one assertion per action)","Check spelling/case of the action string against the CLOUD_CONTROL_ACTIONS allowlist in cloud-runtime-identity.ts","If a genuinely new action is needed, add it to CLOUD_CONTROL_ACTIONS in code and redeploy, then mint tokens for it","Stop replaying a single broad assertion across endpoints; generate per-request assertions"],"exampleFix":"// before\nconst assertion = mintAssertion({ sub: stackId, action: \"logs\", requestId });\nawait control(\"restart\", assertion);\n// after\nconst assertion = mintAssertion({ sub: stackId, action: \"restart\", requestId: crypto.randomUUID() });\nawait control(\"restart\", assertion);","handlingStrategy":"validation","validationCode":"const ACTIONS = [/* mirror of CLOUD_CONTROL_ACTIONS */];\nfunction authorizeBeforeMint(action, expectedAction) {\n  if (!ACTIONS.includes(action)) throw new Error(`action ${action} not in allowlist`);\n  if (action !== expectedAction) throw new Error(`mint action ${action} but endpoint expects ${expectedAction}`);\n}\nauthorizeBeforeMint(minted.action, \"restart\");","typeGuard":null,"tryCatchPattern":"try {\n  return verifyCloudControlAssertion({ compactJws: token, expectedAction });\n} catch (e) {\n  if (e.message === \"Cloud control assertion does not authorize this action\") {\n    return respond(403, \"insufficient action scope\"); // mint a fresh assertion for this specific action\n  }\n  throw e;\n}","preventionTips":["Mint one assertion per request/action; never reuse a token across different control endpoints","Import the action allowlist from shared constants instead of hardcoding strings","Add an integration test per (action, endpoint) pair"],"tags":["security","authorization","jws","least-privilege"],"backgroundTag":"permission-denied","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T10:30:35.592Z"}