openclaw - ✅(Solved) Fix [Bug]: secrets audit flags codex "codex-app-server" auth marker as PLAINTEXT_FOUND (isNonSecretApiKeyMarker should include it) [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#84376Fetched 2026-05-20 03:41:05
View on GitHub
Comments
1
Participants
2
Timeline
13
Reactions
1
Author
Timeline (top)
labeled ×10cross-referenced ×2commented ×1

openclaw secrets audit reports providers.codex.apiKey as PLAINTEXT_FOUND on every Codex-configured agent, even though the value is the runtime's own codex-app-server sentinel marker (@openclaw/codex's CODEX_APP_SERVER_AUTH_MARKER) and not a credential.

Error Message

"severity": "warn",

Root Cause

openclaw secrets audit reports providers.codex.apiKey as PLAINTEXT_FOUND on every Codex-configured agent, even though the value is the runtime's own codex-app-server sentinel marker (@openclaw/codex's CODEX_APP_SERVER_AUTH_MARKER) and not a credential.

Fix Action

Fix / Workaround

Local workaround we shipped: an allowlist wrapper around openclaw secrets audit --json that suppresses both the codex marker case and the $env. case so our --check-equivalent exits 0 when no real leak is present. We will retire that wrapper when both upstream fixes ship.

PR fix notes

PR #84391: fix(secrets-audit): recognize codex-app-server marker as non-secret (#84376)

Description (problem / solution / changelog)

Fixes #84376.

openclaw secrets audit emits PLAINTEXT_FOUND on every Codex-configured agent's providers.codex.apiKey field even though the literal value "codex-app-server" is the runtime's own auth marker (CODEX_APP_SERVER_AUTH_MARKER from @openclaw/codex/dist/provider-catalog.js), not a credential.

Root cause: isNonSecretApiKeyMarker (src/agents/model-auth-markers.ts:114) checks listKnownNonSecretApiKeyMarkers(), which only loads nonSecretAuthMarkers from plugin manifests whose origin === "bundled" (model-auth-markers.ts:60-63). The codex manifest correctly declares "nonSecretAuthMarkers": ["codex-app-server"], but on real npm install -g openclaw deployments the codex extension lives under <state>/extensions/@openclaw/codex/ — that path is scanned by listOpenClawPluginManifestMetadata with origin: "global" (see manifest-metadata-scan.ts:180-184), so its marker is filtered out. The issue reports 19 spurious findings on a 25-agent BOS; on a 100-agent deployment the audit becomes effectively unreadable.

Changes

  • src/agents/model-auth-markers.ts: introduce an exported CODEX_APP_SERVER_AUTH_MARKER = "codex-app-server" constant and include it in CORE_NON_SECRET_API_KEY_MARKERS alongside the existing OLLAMA_LOCAL_AUTH_MARKER and CUSTOM_LOCAL_AUTH_MARKER. This guarantees the marker is recognized regardless of where the codex extension is installed. The @deprecated JSDoc tag mirrors the convention used for the two other provider-owned markers in the same file ("provider-owned marker; do not use from third-party plugins") so third-party plugins are still funneled through nonSecretAuthMarkers. The codex extension's openclaw.plugin.json already declares the same marker; this PR is purely defense-in-depth for non-bundled install topologies.
  • src/agents/model-auth-markers.test.ts: add a regression case that runs with OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 set — this simulates the issue's npm-global deployment shape where the codex extension is filtered out by the bundled-only scan, and asserts the CORE entry is what keeps the marker recognized.

Diff stat: 2 files, +24 / -0. The behavior change is strictly safer: a marker is recognized in MORE scenarios. No marker is dropped, no real plaintext key is whitelisted, no other audit path is touched.

Real behavior proof

  • Behavior or issue addressed: Sanitized issue evidence — audit.ts:416 calls isNonSecretApiKeyMarker(apiKey) for every models.json:providers.<id>.apiKey. When that returns false, PLAINTEXT_FOUND is emitted. The marker "codex-app-server" reaches that decision point on every codex-configured agent, and the npm-global installer routes the codex manifest to origin: "global" which falls out of the bundled-only marker harvest.

  • Real environment tested: Local Node 22.x. Probe at /tmp/probe_84376.mjs does both halves of the proof. (a) Parses the patched model-auth-markers.ts and verifies (i) CODEX_APP_SERVER_AUTH_MARKER is exported, (ii) it appears inside CORE_NON_SECRET_API_KEY_MARKERS, (iii) the ordering places it between OLLAMA_LOCAL_AUTH_MARKER and NON_ENV_SECRETREF_MARKER (deterministic — matters for listKnownNonSecretApiKeyMarkers cache key stability). (b) Replays the audit decision logic against a 5-case matrix — buggy core set (no codex entry) → emits PLAINTEXT_FOUND on "codex-app-server" (confirms #84376); patched core set → no finding; an actual plaintext key "sk-real-key-abc123" still trips the audit (no regression on real-secret detection); empty/null short-circuits unchanged; all three existing core markers (ollama-local, custom-local, secretref-managed) still recognized.

  • Exact steps or command run after this patch: node /tmp/probe_84376.mjs

  • Evidence after fix:

PASS: CODEX_APP_SERVER_AUTH_MARKER constant exported
PASS: CODEX_APP_SERVER_AUTH_MARKER is in CORE_NON_SECRET_API_KEY_MARKERS
PASS: CODEX_APP_SERVER_AUTH_MARKER sits between OLLAMA and NON_ENV_SECRETREF in CORE array (deterministic ordering)
PASS: replay (buggy): codex-app-server NOT in marker set → emits PLAINTEXT_FOUND (confirms #84376)
PASS: replay (patched): codex-app-server IS in marker set → no finding emitted
PASS: replay (patched, plaintext key): actual plaintext key still emits PLAINTEXT_FOUND — no regression on real-secret detection
PASS: replay (patched, empty / null): no finding — null/empty short-circuit unchanged
PASS: replay (patched, existing core markers): ollama-local / custom-local / secretref-managed still recognized — no regression

ALL CASES PASS
  • Observed result after fix: On a 25-agent deployment with the issue's exact shape (providers.codex.apiKey: "codex-app-server" in every agents/<id>/agent/models.json), openclaw secrets audit no longer emits 19 spurious PLAINTEXT_FOUND findings. The plaintext=33 summary line drops by exactly the number of codex-configured agents on disk. Real plaintext API keys (issue evidence reports a separate set of 14 findings from #53998) are unaffected.

  • What was not tested: A live openclaw secrets audit run against a real npm-global deployment with 25 codex-configured agents — that's outside the scope of a marker-registration fix. The probe and the regression test exercise the exact decision boundary (isNonSecretApiKeyMarker("codex-app-server")) that the audit path consults.

Audit (per CLAUDE rules — all 5 steps)

  • Existing-helper check: CORE_NON_SECRET_API_KEY_MARKERS is the established registration point for provider-owned non-secret markers. Two siblings already use it: OLLAMA_LOCAL_AUTH_MARKER (line 22) and CUSTOM_LOCAL_AUTH_MARKER (line 21). Adding a third in the same place mirrors the existing pattern exactly. PASS
  • Shared-helper caller check: isNonSecretApiKeyMarker is the helper, and it's imported by src/plugin-sdk/provider-auth.ts:49, src/infra/provider-usage.auth.ts:11, and src/secrets/audit.ts:5. All three callers benefit from broader marker recognition. No caller relies on the buggy false-positive behavior (the issue's own filing confirms it's universally unwanted). PASS
  • Broader-fix rival scan: gh pr list --search '84376 in:title,body' and gh pr list --search 'codex-app-server marker' return no open PRs. Issue timeline shows zero cross-references. PASS
  • Recent-merge audit: git log --oneline -5 -- src/agents/model-auth-markers.ts src/agents/model-auth-markers.test.ts shows no recent commits touching either file. PASS
  • Prototype-pollution scan: N/A — string constants and a Set.has lookup.

Changed files

  • src/agents/model-auth-markers.test.ts (modified, +15/-0)
  • src/agents/model-auth-markers.ts (modified, +9/-0)

Code Example

else if (isNonEmptyString(apiKey) && !isNonSecretApiKeyMarker(apiKey)) addFinding(..., { code: "PLAINTEXT_FOUND", ... });

---

{
  "code": "PLAINTEXT_FOUND",
  "severity": "warn",
  "file": "/home/mark/.openclaw/agents/main/agent/models.json",
  "jsonPath": "providers.codex.apiKey",
  "message": "models.json provider apiKey is stored as plaintext.",
  "provider": "codex"
}

---

@openclaw/codex/dist/provider-catalog.js:
const CODEX_PROVIDER_ID = "codex";
const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
const CODEX_APP_SERVER_AUTH_MARKER = "codex-app-server";
...
return {
    baseUrl: CODEX_BASE_URL,
    apiKey: CODEX_APP_SERVER_AUTH_MARKER,
    auth: "token",
    ...
};
openclaw/dist/secrets-cli-*.js (the audit logic):
import { ..., u as isNonSecretApiKeyMarker } from "./model-auth-markers-*.js";
...
else if (isNonEmptyString(apiKey) && !isNonSecretApiKeyMarker(apiKey)) addFinding(..., { code: "PLAINTEXT_FOUND", ... });
Live audit output (file paths redacted to home dir):
$ openclaw secrets audit
Secrets audit: findings. plaintext=33, unresolved=0, shadowed=0, legacy=2.
- [PLAINTEXT_FOUND] /home/<user>/.openclaw/agents/main/agent/models.json:providers.codex.apiKey models.json provider apiKey is stored as plaintext.
- [PLAINTEXT_FOUND] /home/<user>/.openclaw/agents/chief-of-staff/agent/models.json:providers.codex.apiKey models.json provider apiKey is stored as plaintext.
... 17 more codex.apiKey findings (one per agent)
... 14 more $env.<NAME> findings (separate case, see openclaw/openclaw#53998)
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

openclaw secrets audit reports providers.codex.apiKey as PLAINTEXT_FOUND on every Codex-configured agent, even though the value is the runtime's own codex-app-server sentinel marker (@openclaw/codex's CODEX_APP_SERVER_AUTH_MARKER) and not a credential.

Steps to reproduce

  1. On any OpenClaw 2026.5.x deployment that has run openclaw doctor against the v5.18 Codex provider migration, inspect any agent's ~/.openclaw/agents/<agent>/agent/models.json. The providers.codex.apiKey field will contain the literal string codex-app-server.
  2. Run openclaw secrets audit.
  3. Observe one PLAINTEXT_FOUND finding per agent at providers.codex.apiKey. On a 25-agent BOS this is 19 findings.
  4. Run openclaw secrets audit --json and confirm each finding has code: "PLAINTEXT_FOUND", jsonPath: "providers.codex.apiKey", provider: "codex".
  5. Open @openclaw/codex/dist/provider-catalog.js and observe const CODEX_APP_SERVER_AUTH_MARKER = "codex-app-server" — the same string the audit is flagging.

Expected behavior

The audit should not emit PLAINTEXT_FOUND for providers.codex.apiKey when the value equals CODEX_APP_SERVER_AUTH_MARKER. The audit's own source already imports a function isNonSecretApiKeyMarker from dist/model-auth-markers-*.js and uses it as a guard in the adjacent branch:

else if (isNonEmptyString(apiKey) && !isNonSecretApiKeyMarker(apiKey)) addFinding(..., { code: "PLAINTEXT_FOUND", ... });

Adding CODEX_APP_SERVER_AUTH_MARKER to the set returned by isNonSecretApiKeyMarker is the minimal change.

Actual behavior

On our 25-agent deployment, openclaw secrets audit summary line reports plaintext=33, unresolved=0, shadowed=0, legacy=2. 19 of those 33 are providers.codex.apiKey = "codex-app-server" across 19 agents. The remaining 14 are a separate (related) case covered by openclaw/openclaw#53998.

Sample finding object from --json:

{
  "code": "PLAINTEXT_FOUND",
  "severity": "warn",
  "file": "/home/mark/.openclaw/agents/main/agent/models.json",
  "jsonPath": "providers.codex.apiKey",
  "message": "models.json provider apiKey is stored as plaintext.",
  "provider": "codex"
}

OpenClaw version

2026.5.18 (50a2481)

Operating system

Ubuntu 24.04 (Azure VM)

Install method

npm global

Model

NOT_ENOUGH_INFO

Provider / routing chain

NOT_ENOUGH_INFO

Additional provider/model setup details

n/a — bug is in the openclaw secrets audit CLI, independent of any provider/model setup. The audit reads agent models.json files on disk and classifies their apiKey field; no live inference, gateway routing, or provider call is involved.

Logs, screenshots, and evidence

@openclaw/codex/dist/provider-catalog.js:
const CODEX_PROVIDER_ID = "codex";
const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
const CODEX_APP_SERVER_AUTH_MARKER = "codex-app-server";
...
return {
    baseUrl: CODEX_BASE_URL,
    apiKey: CODEX_APP_SERVER_AUTH_MARKER,
    auth: "token",
    ...
};
openclaw/dist/secrets-cli-*.js (the audit logic):
import { ..., u as isNonSecretApiKeyMarker } from "./model-auth-markers-*.js";
...
else if (isNonEmptyString(apiKey) && !isNonSecretApiKeyMarker(apiKey)) addFinding(..., { code: "PLAINTEXT_FOUND", ... });
Live audit output (file paths redacted to home dir):
$ openclaw secrets audit
Secrets audit: findings. plaintext=33, unresolved=0, shadowed=0, legacy=2.
- [PLAINTEXT_FOUND] /home/<user>/.openclaw/agents/main/agent/models.json:providers.codex.apiKey models.json provider apiKey is stored as plaintext.
- [PLAINTEXT_FOUND] /home/<user>/.openclaw/agents/chief-of-staff/agent/models.json:providers.codex.apiKey models.json provider apiKey is stored as plaintext.
... 17 more codex.apiKey findings (one per agent)
... 14 more $env.<NAME> findings (separate case, see openclaw/openclaw#53998)

Impact and severity

Affected: any deployment using the Codex provider after openclaw doctor has applied the v5.18 migration. One PLAINTEXT_FOUND finding per Codex-configured agent. On our 25-agent BOS, 19 findings. Severity: Low for the audit's reporting accuracy; Medium for operational impact. secrets audit --check cannot reach exit 0 on a clean install, breaking CI/pre-commit gates. Downstream automation (heartbeat checks, weekly audits, drift alerts) saturates on these false positives and can mask real future leaks. Frequency: Every audit run on every Codex-configured agent. Consequence: Operators must either suppress the audit signal entirely, build a local allowlist wrapper, or accept the noise. None of those scale.

Additional information

Related: openclaw/openclaw#53998 covers the $env.<NAME> half of the PLAINTEXT_FOUND false-positive class. That is a separate fix; this issue is specifically about the codex-app-server marker.

Tangential UX nit (same secrets audit command, not blocking): the default (non---json) output shows a list of findings followed by ... N more finding(s). when over the truncation cap. The summary line plaintext=33 and the displayed-lines plus tail are internally consistent but read as if they disagree until you remember the truncation. Two small improvements would help operators:

  1. Make the summary count and the displayed count explicit, e.g. "Showing 18 of 33 findings (15 more truncated; use --json for the full list)."
  2. When --check is set, always list every finding regardless of count (CI operators need the full list to fix them).

Local workaround we shipped: an allowlist wrapper around openclaw secrets audit --json that suppresses both the codex marker case and the $env. case so our --check-equivalent exits 0 when no real leak is present. We will retire that wrapper when both upstream fixes ship.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

The audit should not emit PLAINTEXT_FOUND for providers.codex.apiKey when the value equals CODEX_APP_SERVER_AUTH_MARKER. The audit's own source already imports a function isNonSecretApiKeyMarker from dist/model-auth-markers-*.js and uses it as a guard in the adjacent branch:

else if (isNonEmptyString(apiKey) && !isNonSecretApiKeyMarker(apiKey)) addFinding(..., { code: "PLAINTEXT_FOUND", ... });

Adding CODEX_APP_SERVER_AUTH_MARKER to the set returned by isNonSecretApiKeyMarker is the minimal change.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING