{"record":{"id":"132bbe7a40acb621","repo":"unicity-aos/aos-ce","slug":"hook-adapter-oracle-dropping-principal-mismatch-for-expected","errorCode":null,"errorMessage":"hook-adapter-oracle: dropping principal mismatch for {expected.name()}","messagePattern":"hook-adapter-oracle: dropping principal mismatch for (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"capsules/capsule-hook-adapter-oracle/src/lib.rs","lineNumber":383,"sourceCode":"                expected.name()\n            ));\n            return Ok(());\n        }\n    };\n    let mapping = match validate_oracle_hook(expected, &event) {\n        Ok(mapping) => mapping,\n        Err(reason) => {\n            log::warn(format!(\n                \"hook-adapter-oracle: dropping invalid {} hook '{}': {reason}\",\n                expected.name(),\n                event.event\n            ));\n            return Ok(());\n        }\n    };\n    let caller = runtime::caller()?;\n    if caller.principal.as_deref() != Some(event.principal_id.as_str()) {\n        log::warn(format!(\n            \"hook-adapter-oracle: dropping principal mismatch for {}\",\n            expected.name()\n        ));\n        return Ok(());\n    }\n\n    let context = dispatch_oracle_hook(&event, mapping)?;\n    ipc::publish_json(\n        &format!(\"oracle.v1.hook.response.{}\", event.delivery_id),\n        &OracleHookResponse {\n            schema_version: 1,\n            principal_id: &event.principal_id,\n            host: &event.host,\n            session_id: &event.session_id,\n            canonical_hook: mapping.hook,\n            event: &event.event,\n            correlation_id: &event.correlation_id,\n            route_id: &event.route_id,","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-hook-adapter-oracle/src/lib.rs#L365-L401","documentation":"handle_oracle_hook compares the authenticated caller principal from runtime::caller() against the principal_id embedded in the hook event. If they differ, the event is rejected with this warning and dropped. This prevents a caller from spoofing another principal's identity in hook events — the event's claimed principal must match the actual verified caller.","triggerScenarios":"Raised in handle_oracle_hook when event.principal_id (a string taken from the hook payload) does not equal the verified caller.principal returned by runtime::caller(). Any code path on_codex_hook/on_claude_hook/on_grok_hook invoking the adapter with a payload whose principal_id field was set independently of the actual caller triggers it.","commonSituations":"A hook producer hardcodes or misconfigures principal_id in the event payload; the hook is invoked through an intermediary/bridge that forwards a stale or different principal; identity rotation or re-auth means the payload's principal_id is outdated relative to the live caller identity.","solutions":["Set event.principal_id from the actual authenticated caller identity at the point of emission rather than hardcoding it.","Re-authenticate / refresh the caller identity so the live principal matches the one encoded in the hook payload.","If a bridge forwards hook events, ensure it preserves or rewrites principal_id to the forwarded caller's verified principal.","Check the warning context to compare the two principals and correct whichever is stale."],"exampleFix":"// before\nlet event = OracleHookEvent { principal_id: \"user-alice\".into(), .. };\ndispatch(event); // invoked as user-bob\n// after\nlet event = OracleHookEvent { principal_id: current_verified_principal(), .. };\ndispatch(event);","handlingStrategy":"validation","validationCode":"// Confirm the caller principal before dispatching a hook event\nlet caller = runtime::caller()?;\nassert_eq!(caller.principal.as_deref(), Some(event.principal_id.as_str()), \"principal_id in payload must match verified caller\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always derive event.principal_id from the verified runtime caller identity, never hardcode it.","Refresh credentials/identity before long-running processes emit hooks.","Ensure intermediaries (bridges) forward or rewrite principal_id to the actual caller."],"tags":["rust","authentication","principal","authorization"],"backgroundTag":"permission-denied","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}