Back to skill

Security audit

codex-history-manager

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a disclosed local Codex history manager, but it includes high-impact history rewriting and bulk metadata changes with guardrail weaknesses that users should review carefully.

Install only if you need local Codex history administration and are comfortable granting a tool access to read, export, and mutate your ~/.codex history. Treat all exports and handoffs as potentially sensitive, run dry runs first, review scope counts carefully, avoid change-provider-all unless intended, and do not use apply-dangerous-edit unless you have independently reviewed the exact plan file immediately before applying it.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/codex_history_manager.py:695
Finding

Dangerous edit plan identifier is not recomputed before history modification

Content
View full analysis
tuple[int, int]: find = str(plan["find"]) replace = str(plan["replace"]) title, title_count = replace_literal(thread.title, find, replace) first_user_message, first_count = replace_literal(thread.first_user_message, find, replace) conn.execute( "update threads set title = ?, first_user_message = ?, updated_at = ? where id = ?", (title, first_user_message, now_timestamp(), thread.id), ) conn.commit() ``` ```python plan_path = args.plan.expanduser().resolve() plan = json.loads(plan_path.read_text(encoding="utf-8")) plan_id = plan.get("plan_id") if not isinstance(plan_id, str) or plan_id != args.confirm_plan_id: raise CodexHistoryError("Confirmation failed: --confirm-plan-id doe ...[truncated 3237 chars]
Remediation
View remediation
str: plan_core = { "thread_id": plan["thread"]["id"], "rollout_path": plan["thread"]["rollout_path"], "find": plan["find"], "replace": plan["replace"], "change_count": plan["change_count"], "total_occurrences": plan["total_occurrences"], } return hashlib.sha256( json.dumps( plan_core, ensure_ascii=False, sort_keys=True, separators=(",", ":"), ).encode("utf-8") ).hexdigest() ``` 2. Reject application unless all three values match: - The identifier recomputed from the loaded plan - The identifier stored in the plan - The identifier supplied through `--confirm-plan-id` 3. Use the complete SHA-256 digest rather than truncating it to 16 hexadecimal characters. 4. Regenerate the dangerous-edit plan against the current SQLite row and rollout file immediately before writing. Compare the complete change list and occurrence counts with those approved in the saved plan. 5. Include hashes of the source database fields and rollout file in the plan. Refuse application if either source changed after planning. 6. Validate plan field types strictly rather than coercing arbitrary values through `str()`. 7. If plan files may cross trust boundaries, authenticate them with an HMAC stored separately from the plan or a digital signature. A plain hash only detects changes when the application independently recomputes and verifies it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/codex_history_manager.py:875
Finding

Untrusted rollout paths are not confined to approved Codex storage directories

Content
View full analysis
"ThreadRecord": payload = dict(row) payload["rollout_path"] = Path(payload["rollout_path"]) return cls(**payload) ``` ```python def iter_rollout_records(path: Path) -> Iterator[dict[str, Any]]: with path.open("r", encoding="utf-8") as handle: for line_no, raw_line in enumerate(handle, start=1): try: yield json.loads(raw_line) except json.JSONDecodeError as exc: raise CodexHistoryError(f"Invalid JSON in {path}:{line_no}: {exc}") from exc ``` ```python def backup_rollouts(codex_home: Path, rollout_paths: list[Path], target_dir: Path) -> Path: tar_path = target_dir / "rollouts-before.tar.gz" manifest_path = target_dir / "rollout-files.txt" manifest_path.write_text("\n".join(str(path) for path in rollout_paths) + "\n", encoding="utf-8") resolved_home = codex_home.resolve() with tarfile.open(tar_path, "w:gz") as archive: for rollout_path in rollout_paths: resolved_rollout = rollout_path.resolve() try: arcname = str(resolved_rollout.relative_to(resolved_home)) except ValueError: arcname = rollout_path.name archive.add(rollout_path, arcname=arcname) return tar_path ``` ```python def replace_in_place(path: Path, transform: Callable[[dict[str, Any]], dict[str, Any] | None]) -> int: with tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False, dir=path.parent) as temp_file: temp_path = Path(temp_file.name) changed = rewrite_jsonl_file(path, temp_path, transform) temp_path.replace(path) return changed ``` ### Technical Analy ...[truncated 2513 chars]
Remediation
View remediation
Path: candidate = value.expanduser().resolve(strict=True) allowed_roots = [ (codex_home / "sessions").resolve(), (codex_home / "archived_sessions").resolve(), ] if not any(candidate.is_relative_to(root) for root in allowed_roots): raise CodexHistoryError( f"Rollout path is outside approved Codex storage: {candidate}" ) if candidate.is_symlink() or not candidate.is_file(): raise CodexHistoryError( f"Rollout path must be a regular, non-symlink file: {candidate}" ) return candidate ``` 2. Apply this validation immediately after every `rollout_path` is loaded from SQLite and again immediately before reading, archiving, cloning, or replacing the file. 3. Reject paths outside the approved roots rather than falling back to `rollout_path.name` in `backup_rollouts()`. 4. Check every path in batch operations before creating backups or committing database changes. Abort the entire operation if any path is invalid. 5. Use race-resistant file operations where supported: - Open files with no-follow semantics such as `O_NOFOLLOW` - Verify file descriptors with `fstat` - Avoid validating a path and reopening it later by name 6. Preserve original file permissions when replacing rollout files, and explicitly set restrictive permissions on temporary files and backups. 7. Treat a caller-provided `--codex-home` as a separate trust boundary. Clearly document that it must point to trusted local state and validate its ownership and permissions before applying mutations. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description presents the skill primarily as a history inspection and management utility, but the body also exposes permanent content-rewrite capability through plan-dangerous-edit and apply-dangerous-edit. Because Codex history may be relied on for auditability, handoff integrity, or provenance, an undeclared rewrite feature can be abused to tamper with transcripts, conceal prior actions, or alter evidence while appearing to operate as a benign history tool.

Content

No source excerpt is available for this finding.

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · references/commands.md (reported line 73)May include surrounding context.

bash
./codex-history-manager change-provider --id <thread-id> --provider openai1 --dry-run
./codex-history-manager change-provider --id <thread-id> --provider openai1 --apply
./codex-history-manager change-provider --id <thread-id> --provider openai1 --model gpt-5.4 --apply
./codex-history-manager change-provider-workspace --cwd /abs/path --provider openai1 --dry-run
./codex-history-manager change-provider-workspace --cwd /abs/path --provider openai1 --apply
./codex-history-manager change-provider-all --provider openai1 --dry-run

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 74)May include surrounding context.

md
- Managing remote ChatGPT / Claude / Gemini web history
- Modifying server-side history stored by OpenAI or Anthropic
- Bulk rewriting chat content without confirmation
- Acting as a generic database repair tool for arbitrary internal fields

## Common Commands

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill clearly instructs the agent to read from and write to local Codex history under ~/.codex and to export data to arbitrary output paths, yet it declares no explicit tool scope or permission boundaries. That omission increases the risk of overbroad filesystem access and makes it harder to enforce least privilege, especially because the skill includes mutating operations such as move, clone, provider rebinding, and transcript rewrite workflows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documented search feature explicitly searches thread titles, first user messages, and visible transcript messages, which can contain sensitive conversation history. Without an explicit privacy warning or guidance to confirm user intent before searching or displaying results, the skill increases the risk of unintentional disclosure of local transcript data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Export and handoff commands write conversation history and derived summaries to arbitrary filesystem paths, creating a straightforward path for sensitive data to be copied into less protected locations. The commands are legitimate, but the documentation lacks user-facing warnings about data disclosure, persistence, and safe destination handling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The apply examples for moving, cloning, and reassigning thread/workspace/provider metadata are state-changing operations that can alter history associations at scale. Although some dry-run examples are shown, the documentation does not consistently instruct agents to obtain explicit approval before running --apply, which raises the risk of unintended or irreversible metadata changes.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/safety.md (reported line 3)May include surrounding context.

md
# Safety

- Write commands default to dry run unless `--apply` is present.
- Every write command creates a timestamped backup directory under `backups/`.
- Database backup: full copy of `~/.codex/state_5.sqlite`
- Rollout backup: tarball with every affected rollout file plus a text manifest

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The safety file documents an apply-dangerous-edit capability that rewrites stored history content, which materially exceeds the skill’s stated scope of inspecting, exporting, handing off, cloning, moving, or rebinding metadata. Even though the doc adds warnings and approval gates, the existence of a content-rewrite path increases the attack surface because an agent could be induced to alter historical records, transcripts, or provenance-sensitive data under the guise of history management.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill metadata says it can search, read, export, hand off, clone, move, or rebind history, but the code also exposes plan/apply commands that permanently rewrite stored transcript content. That hidden destructive capability materially changes the trust model: an agent or user may invoke the skill assuming it is archival/administrative, while it can actually alter evidence, provenance, and future context derived from history.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The change-provider-all command enables bulk mutation of provider metadata for every local Codex thread, which exceeds the narrower thread/workspace-focused expectations set by the skill description. A single invocation can silently rewrite metadata at large scope, causing misattribution, broken downstream assumptions, or broad corruption of local historical records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The export-thread and handoff commands write full or recent conversation content, including potentially sensitive user messages, assistant responses, workspace paths, and metadata, to arbitrary user-specified files with no privacy warning, redaction step, or destination restrictions. In a history-management skill, transcripts are especially likely to contain secrets, personal data, or internal paths, so easy exfiltration to disk increases confidentiality risk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.