{"record":{"id":"7281ecdd08441d59","repo":"BigPizzaV3/CodexPlusPlus","slug":"sidebar-catalog-database-is-not-an-allowed-codex-database","errorCode":null,"errorMessage":"sidebar catalog database is not an allowed Codex database","messagePattern":"sidebar catalog database is not an allowed Codex database","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/provider_sync.rs","lineNumber":2718,"sourceCode":"            .as_array()\n            .ok_or_else(|| anyhow::anyhow!(\"sidebar snapshot catalog must be an array\"))?;\n        let allowed_paths = sidebar_catalog_db_paths(codex_home)?;\n        for entry in entries {\n            let table = entry\n                .get(\"table\")\n                .and_then(Value::as_str)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry is missing table\"))?;\n            if !SIDEBAR_CATALOG_TABLES.contains(&table) {\n                anyhow::bail!(\"unsupported sidebar catalog table: {table}\");\n            }\n            let path = entry\n                .get(\"db_path\")\n                .and_then(Value::as_str)\n                .map(PathBuf::from)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry is missing db_path\"))?;\n            let canonical = fs::canonicalize(&path)?;\n            if !allowed_paths.contains(&canonical) {\n                anyhow::bail!(\"sidebar catalog database is not an allowed Codex database\");\n            }\n            let rows = entry\n                .get(\"rows\")\n                .and_then(Value::as_array)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry rows must be an array\"))?;\n            for row in rows {\n                let row_id = row\n                    .get(\"thread_id\")\n                    .and_then(Value::as_str)\n                    .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog row is missing thread_id\"))?;\n                if row_id != thread_id {\n                    anyhow::bail!(\"sidebar catalog row thread_id does not match snapshot\");\n                }\n            }\n        }\n    }\n    Ok(())\n}","sourceCodeStart":2700,"sourceCodeEnd":2736,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-data/src/provider_sync.rs#L2700-L2736","documentation":"Each sidebar catalog entry's db_path, after canonicalization, must be present in the allowlist of Codex database paths. This error blocks restore operations against any SQLite file that is not a recognized Codex database, guarding against arbitrary-file overwrites.","triggerScenarios":"Restoring sidebar state where an entry's 'db_path' canonicalizes to a path not in allowed_paths (external DB, symlinked path, moved session storage, or path spelled differently from the canonical form).","commonSituations":"User relocated the Codex sessions directory; db_path contains a symlink or relative path resolving elsewhere; state file copied between machines with different user homes.","solutions":["Set db_path to the actual Codex sessions database location for this machine (let the app re-derive it or fix the JSON)","Remove symlinks in the path so canonicalize() yields the expected directory, or update the allowlist source to include the new canonical location","If the state came from another machine, re-sync sidebar state locally instead of restoring the foreign db_path"],"exampleFix":"// before\n{\"table\":\"threads\",\"db_path\":\"C:\\\\old\\\\sessions\\\\state.db\"}\n// after\n{\"table\":\"threads\",\"db_path\":\"C:\\\\Users\\\\me\\\\.codex\\\\sessions\\\\state.db\"}","handlingStrategy":"validation","validationCode":"let canonical = std::fs::canonicalize(&db_path)?;\nif !allowed_paths.contains(&canonical) {\n    eprintln!(\"db_path {} is not an allowed Codex database\", canonical.display());\n}","typeGuard":"fn allowed_db(p: &Path, allowed: &[PathBuf]) -> bool {\n    p.canonicalize().map(|c| allowed.contains(&c)).unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = restore(&state) {\n    if e.to_string().contains(\"not an allowed Codex database\") {\n        // re-derive db_path locally and rewrite the entry before retrying\n    }\n}","preventionTips":["Derive db_path from the app's own session-dir resolution, not from foreign state files","Avoid symlinks inside the sessions path so canonicalization is predictable","Re-sync state per machine instead of copying global state across hosts"],"tags":["security","path","allowlist"],"backgroundTag":"path-traversal-blocked","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}