Skip to content

Secrets: harden SecretRef-safe models.json persistence#38955

Merged
joshavant merged 16 commits intomainfrom
feat/secretref-models-persistence-hardening-2
Mar 7, 2026
Merged

Secrets: harden SecretRef-safe models.json persistence#38955
joshavant merged 16 commits intomainfrom
feat/secretref-models-persistence-hardening-2

Conversation

@joshavant
Copy link
Copy Markdown
Contributor

@joshavant joshavant commented Mar 7, 2026

Summary

This PR implements a single end-to-end fix path for the models.json plaintext-persistence cluster and related hardening surfaced in the 2026-03-06 external secrets audit.

Primary cluster addressed:

Problem surface addressed

  • Resolved SecretRef credentials could still be persisted into agents/*/agent/models.json.
  • Merge behavior could preserve stale/resolved apiKey values even when a provider is SecretRef-managed.
  • Command paths (not just gateway startup/reload paths) could miss source-snapshot persistence behavior.
  • Audit/apply coverage needed to include generated models.json residues for provider apiKey and sensitive provider headers.
  • Marker leakage safeguards needed to ensure marker placeholders are not used as real auth on discovery/request paths.
  • models.json write hardening needed deterministic serialization + file mode enforcement.

What changed

  • Added provenance-aware auth marker utilities and centralized marker handling in model auth normalization.
  • Tightened marker detection to avoid misclassifying arbitrary all-caps API keys:
    • removed broad env-var-shape marker heuristic
    • marker detection now accepts explicit markers + bounded known env marker names only
    • added regression tests ensuring uppercase real keys are still treated as credentials
  • Updated models.mode=merge behavior:
    • preserve existing apiKey only when provider is not SecretRef-managed in current config/auth-profile context
    • SecretRef-managed providers refresh from source markers instead of preserving stale key material
  • Added runtime source snapshot plumbing for command-driven persistence:
    • source snapshot accessor + runtime snapshot/source snapshot support in config IO
    • models command load path uses source+resolved snapshot pairing
    • openclaw agent path now sets runtime snapshot with source snapshot before embedded runner path
  • Hardened request/auth usage paths:
    • skip marker-backed auth for discovery/request calls so placeholders are not emitted as credentials
    • clarified marker-header intent in media-understanding runtime header sanitization path
  • Expanded secrets surface coverage:
    • SecretRef support for models.providers.*.headers.*
    • secrets audit/apply scans and remediates generated models.json residues for apiKey + sensitive headers
  • Hardened models.json writes:
    • deterministic serialization behavior
    • enforce 0600 mode on write and no-content-change paths
    • hardened write-lock control flow (await prior now inside try/finally)
  • Updated docs/schema help to match new merge + persistence semantics and audit coverage.

Regression resistance

Added/updated tests across:

  • models provider auth provenance/normalization
  • runtime source snapshot persistence behavior
  • merge stale-key cleanup semantics
  • marker compatibility behavior and uppercase-key non-marker regressions
  • no marker leakage to discovery auth calls
  • command path source snapshot wiring (openclaw agent)
  • models list/auth overview behavior
  • secrets apply/audit coverage for models.json apiKey and headers
  • models.json serialization + file mode behavior

Validation

Passing focused suites:

  • pnpm test -- src/commands/agent.test.ts src/commands/models/load-config.test.ts src/agents/models-config.providers.auth-provenance.test.ts src/agents/models-config.runtime-source-snapshot.test.ts src/media-understanding/runner.deepgram.test.ts
  • pnpm test -- src/agents/model-auth-markers.test.ts src/infra/provider-usage.auth.normalizes-keys.test.ts src/commands/models/list.probe.targets.test.ts src/secrets/audit.test.ts src/secrets/apply.test.ts
  • pnpm test -- src/agents/models-config.write-serialization.test.ts src/agents/models-config.file-mode.test.ts
  • pnpm check

Notes

  • Runtime credential resolution timing semantics are unchanged:
    • startup: hard fail on misconfiguration
    • reload: last-known-good fallback on resolution failure
  • External secrets management remains fully opt-in; valid plaintext configurations remain supported.

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 7, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Symlink/TOCTOU arbitrary file overwrite & chmod via models.json persistence path

1. 🟡 Symlink/TOCTOU arbitrary file overwrite & chmod via models.json persistence path

Property Value
Severity Medium
CWE CWE-59
Location src/agents/models-config.ts:218-306

Description

The ensureOpenClawModelsJson() persistence hardening introduces/retains unsafe filesystem operations on a path that can be influenced via configuration/environment (agentDirOverride parameter and OPENCLAW_AGENT_DIR / PI_CODING_AGENT_DIR).

If an attacker can influence agentDir (or can create/replace files inside agentDir, e.g., if it is placed in a writable directory), they can exploit symlink following to affect arbitrary files:

  • targetPath is computed as path.join(agentDir, "models.json") with no restriction that agentDir is within a trusted state directory.
  • fs.writeFile(targetPath, ...) follows symlinks, so a pre-created models.json -> /path/to/victim symlink can cause overwriting arbitrary files.
  • Newly added ensureModelsFileMode() calls fs.chmod(targetPath, 0o600) even on the no-content-change path, which can change permissions of the symlink target as well (best-effort, errors ignored), enabling targeted DoS or permission clobbering.
  • No lstat/realpath checks, no O_NOFOLLOW/exclusive create, and no atomic-temp-write + rename pattern are used.

Vulnerable code:

const agentDir = agentDirOverride?.trim() ? agentDirOverride.trim() : resolveOpenClawAgentDir();
const targetPath = path.join(agentDir, "models.json");
...
await fs.writeFile(targetPath, next, { mode: 0o600 });
await fs.chmod(targetPath, 0o600).catch(() => {});

Impact depends on deployment, but in any scenario where untrusted input can affect agentDir or untrusted users can write within that directory, this becomes a classic symlink/hardlink attack surface (CWE-59).

Recommendation

Harden models.json writes against symlink/hardlink attacks and restrict the writable location.

  1. Restrict/validate agentDir
  • Prefer not to accept arbitrary agentDirOverride from any untrusted context.
  • Enforce that agentDir resolves under a trusted base directory (e.g., resolveStateDir()/agents/...).
  1. Refuse symlinks and enforce regular file semantics
  • Before chmod/write, lstat() the path and reject if it is a symlink.
  • Also consider verifying the parent directory is not symlinked (realpath on parent) and has safe ownership/permissions.
  1. Use atomic write with a temp file + rename
  • Write to models.json.tmp.<pid> with secure flags/mode, fsync, then rename.

Example (best-effort illustration):

import { constants as FS } from "node:fs";

async function safeWriteModelsJson(targetPath: string, content: string) {// Refuse symlink targets
  try {
    const st = await fs.lstat(targetPath);
    if (st.isSymbolicLink()) {
      throw new Error("Refusing to write models.json via symlink");
    }
  } catch {// ok if missing
  }

  const dir = path.dirname(targetPath);
  await fs.mkdir(dir, { recursive: true, mode: 0o700 });

  const tmpPath = `${targetPath}.tmp.${process.pid}`;
  const fh = await fs.open(tmpPath, FS.O_CREAT | FS.O_TRUNC | FS.O_WRONLY, 0o600);
  try {
    await fh.writeFile(content, "utf8");
    await fh.sync();
  } finally {
    await fh.close();
  }
  await fs.rename(tmpPath, targetPath);
}
  1. Avoid chmod-on-unchanged-content for untrusted paths
  • Only repair mode after verifying the file is a regular file under a trusted directory; otherwise skip.

Analyzed PR: #38955 at commit 056a7a2

Last updated on: 2026-03-07T18:02:41Z

@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation gateway Gateway runtime commands Command implementations agents Agent runtime and tooling size: XL maintainer Maintainer-authored PR labels Mar 7, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR delivers a comprehensive end-to-end fix for the SecretRef plaintext-persistence cluster. The implementation correctly:

  • Reorders normalize-before-merge in ensureOpenClawModelsJson, ensuring secretRefManagedProviders is populated before merge logic decides whether to preserve stale keys
  • Wires source config snapshots through both the agent and models command paths for proper provenance tracking
  • Replaces the previous broad env-var-shape marker heuristic with a bounded-set marker registry (explicit markers + known env names), verified by regression tests confirming uppercase real keys are treated as credentials
  • Guards request paths against marker values being emitted as real credentials via sanitizeModelHeaders({ stripSecretRefMarkers: true }) in the registry read path
  • Extends secrets audit/apply coverage to models.json API keys and sensitive provider headers
  • Hardens models.json writes with deterministic serialization, 0600 file mode enforcement, and per-path write locking with finally block release guarantee

The security-critical paths (normalize-before-merge ordering, SecretRef-managed key preservation, marker guards, file mode enforcement, write locking) are correctly implemented with comprehensive test coverage. The code is safe to merge.

Confidence Score: 5/5

  • This PR is safe to merge. It comprehensively closes the plaintext-persistence security surface with correct implementation of all hardening measures and no critical logic errors detected.
  • The security-critical paths are correctly implemented: normalize-before-merge ordering ensures SecretRef-managed provider key preservation works correctly, marker detection uses a bounded known-env registry (not broad regex patterns), request paths are guarded against marker leakage, file mode enforcement is deterministic, and write locking with try/finally ensures atomicity. Comprehensive test coverage validates all these hardening measures. No functional or security issues found in code review.
  • No files require special attention.

Last reviewed commit: 146e82f

Comment on lines +48 to +56

for (const agentId of listAgentIds(config)) {
if (agentId === "main") {
paths.add(path.join(resolveUserPath(stateDir), "agents", "main", "agent", "models.json"));
continue;
}
const agentDir = resolveAgentDir(config, agentId);
paths.add(path.join(resolveUserPath(agentDir), "models.json"));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listAgentIds and resolveAgentDir are called in listAgentModelsJsonPaths but not imported. Both functions are exported from ../agents/agent-scope.ts. This causes a ReferenceError at runtime, breaking the entire secrets audit and secrets apply command flow.

Add the missing import at the top of the file:

Suggested change
for (const agentId of listAgentIds(config)) {
if (agentId === "main") {
paths.add(path.join(resolveUserPath(stateDir), "agents", "main", "agent", "models.json"));
continue;
}
const agentDir = resolveAgentDir(config, agentId);
paths.add(path.join(resolveUserPath(agentDir), "models.json"));
}
import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js";

Without this, the listAgentModelsJsonPaths function will fail on any call.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/secrets/storage-scan.ts
Line: 48-56

Comment:
`listAgentIds` and `resolveAgentDir` are called in `listAgentModelsJsonPaths` but not imported. Both functions are exported from `../agents/agent-scope.ts`. This causes a `ReferenceError` at runtime, breaking the entire `secrets audit` and `secrets apply` command flow.

Add the missing import at the top of the file:

```suggestion
import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js";
```

Without this, the `listAgentModelsJsonPaths` function will fail on any call.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +42 to +60
export function isNonSecretApiKeyMarker(
value: string,
opts?: { includeEnvVarName?: boolean },
): boolean {
const trimmed = value.trim();
if (!trimmed) {
return false;
}
const isKnownMarker =
trimmed === MINIMAX_OAUTH_MARKER ||
trimmed === QWEN_OAUTH_MARKER ||
trimmed === OLLAMA_LOCAL_AUTH_MARKER ||
trimmed === NON_ENV_SECRETREF_MARKER ||
isAwsSdkAuthMarker(trimmed);
if (isKnownMarker) {
return true;
}
return opts?.includeEnvVarName === false ? false : isEnvVarNameMarker(trimmed);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isNonSecretApiKeyMarker defaults to treating env-var-like strings as markers when includeEnvVarName is not explicitly false. The regex /^[A-Z][A-Z0-9_]*$/ will match any all-uppercase alphanumeric string — including real API keys from vendors that use this format (e.g., SECRETAPITOKEN).

In src/infra/provider-usage.auth.ts (lines 107, 127, 133), resolveProviderApiKeyFromConfigAndStore calls !isNonSecretApiKeyMarker(key) without options, so legitimate all-uppercase API keys would be incorrectly filtered out as markers, causing silent auth failures.

Consider either:

  1. Adding a length guard to the env-var-name regex (e.g., minimum 8 chars) to reduce false positives on short tokens
  2. Requiring explicit includeEnvVarName: true at persistence/audit call sites, with false as the default for request-path callers

This prevents real credentials from being lost when they happen to match the env-var-name pattern.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-auth-markers.ts
Line: 42-60

Comment:
`isNonSecretApiKeyMarker` defaults to treating env-var-like strings as markers when `includeEnvVarName` is not explicitly `false`. The regex `/^[A-Z][A-Z0-9_]*$/` will match any all-uppercase alphanumeric string — including real API keys from vendors that use this format (e.g., `SECRETAPITOKEN`).

In `src/infra/provider-usage.auth.ts` (lines 107, 127, 133), `resolveProviderApiKeyFromConfigAndStore` calls `!isNonSecretApiKeyMarker(key)` without options, so legitimate all-uppercase API keys would be incorrectly filtered out as markers, causing silent auth failures.

Consider either:
1. Adding a length guard to the env-var-name regex (e.g., minimum 8 chars) to reduce false positives on short tokens
2. Requiring explicit `includeEnvVarName: true` at persistence/audit call sites, with `false` as the default for request-path callers

This prevents real credentials from being lost when they happen to match the env-var-name pattern.

How can I resolve this? If you propose a fix, please make it concise.

@joshavant
Copy link
Copy Markdown
Contributor Author

@greptile-apps Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67ef83311f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +459 to +460
if (!trimmed || isNonSecretApiKeyMarker(trimmed)) {
return undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Accept uppercase discovery keys as real secrets

toDiscoveryApiKey drops any value recognized by isNonSecretApiKeyMarker, and that marker check treats every ^[A-Z][A-Z0-9_]*$ string as an env-var marker. This misclassifies legitimate uppercase API tokens as non-secrets, so provider discovery for vllm/huggingface is invoked without auth (discoveryApiKey becomes undefined), which can cause authenticated /models discovery to fail even when credentials are actually configured.

Useful? React with 👍 / 👎.

Comment on lines +482 to +485
return {
apiKey: resolveNonEnvSecretRefApiKeyMarker(keyRef.source),
source: "non-env-ref",
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep resolved non-env ref keys for provider discovery

When an auth profile has a non-env keyRef/tokenRef plus a resolved runtime secret value, this branch returns only the persistence marker (secretref-managed) and discards the resolved value for discoveryApiKey. Downstream, resolveImplicitProviders uses discoveryApiKey for buildVllmProvider/buildHuggingfaceProvider, so secured discovery endpoints can return unauthenticated results (often empty model catalogs) despite the profile already containing usable runtime credentials.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@mdlmarkham mdlmarkham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Secrets Hardening for models.json ✅

Verdict: Critical security fix. Essential for SecretRef integrity. Ready for merge.

Problem Addressed

SecretRef-managed credentials could still be persisted as plaintext in agents/*/agent/models.json. This defeats the purpose of SecretRef (secrets at rest, resolved at runtime).

Related issues:

Key Fixes

  1. Marker-based persistence: SecretRef-managed apiKey values are persisted as markers (ENV_VAR_NAME, secretref-managed), not resolved secrets.

  2. Merge precedence fix: Non-empty apiKey in models.json only wins when provider is NOT SecretRef-managed.

  3. Audit coverage: secrets audit now scans generated models.json for:

    • Plaintext apiKey values
    • Sensitive provider header residues
  4. Deterministic serialization: Prevents timing attacks via file write patterns.

Security Impact

Before: Resolved secrets persisted on disk → credential exposure risk
After: Only markers persisted → secrets never touch disk in generated files

Test Coverage

The PR includes:

  • Merge precedence tests for SecretRef-managed providers
  • Marker persistence validation
  • Audit integration tests

Edge Cases Handled

  • Provider removed from config but models.json still has stale credentials → audit catches
  • auth-profiles.json shadowing → precedence rules applied
  • Command paths (openclaw agent) → same marker rules as gateway startup

Documentation

Good coverage:

  • CLI docs updated (agent.md, models.md, secrets.md)
  • Concepts updated (models.md, secrets.md)
  • Reference updated (secretref-credential-surface.md)

Minor Observations

  1. +2,356 lines — Large change surface. Recommend careful review of:

    • models-json-snapshot.ts — persistence logic
    • secrets-audit.ts — audit coverage
  2. File mode enforcement — PR mentions "file mode enforcement" but I don't see it in the diff preview. Is this in a separate file?

  3. Backward compatibility — Does this break existing models.json files with plaintext? They should be re-generated on next startup.


Recommendation: Approve. Critical security hardening, addresses real vulnerability in SecretRef persistence.

@joshavant
Copy link
Copy Markdown
Contributor Author

@mdlmarkham Hi! Maintainer here. Please stop. Do not release a code review bot on this project. It is not needed nor helpful.

@joshavant
Copy link
Copy Markdown
Contributor Author

@greptile-apps Review

@joshavant
Copy link
Copy Markdown
Contributor Author

@codex Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04a62df0b2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +15 to +19
const KNOWN_ENV_API_KEY_MARKERS = new Set([
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"ANTHROPIC_OAUTH_TOKEN",
"GEMINI_API_KEY",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include all built-in env markers in allowlist

isNonSecretApiKeyMarker now relies on KNOWN_ENV_API_KEY_MARKERS, but that set omits env var names that resolveEnvApiKey can emit for built-in providers (for example MOONSHOT_API_KEY, VOYAGE_API_KEY, CLOUDFLARE_AI_GATEWAY_API_KEY, HUGGINGFACE_HUB_TOKEN, etc. in src/agents/model-auth.ts). When those providers are env-configured, models.json stores the env var name as the apiKey marker, and this function misclassifies it as plaintext, which causes false secrets audit findings and can make probe/auth paths treat markers as real credentials.

Useful? React with 👍 / 👎.

@joshavant
Copy link
Copy Markdown
Contributor Author

@greptile-apps Review

@joshavant
Copy link
Copy Markdown
Contributor Author

@codex Review

@joshavant joshavant force-pushed the feat/secretref-models-persistence-hardening-2 branch from 4378902 to 146e82f Compare March 7, 2026 16:27
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 146e82f8fa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +631 to +633
configuredApiKeyRef.source === "env"
? configuredApiKeyRef.id.trim()
: resolveNonEnvSecretRefApiKeyMarker(configuredApiKeyRef.source);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Encode env SecretRef apiKey markers unambiguously

normalizeProviders persists env-backed SecretRefs as the raw env var name (configuredApiKeyRef.id.trim()), but downstream marker detection (isNonSecretApiKeyMarker) only recognizes a fixed allowlist of names. For custom env refs (for example models.providers.custom.apiKey: {source:"env", id:"MY_TEAM_TOKEN"}), the persisted value is later treated as plaintext, which triggers false secrets audit leaks and can make probe/auth flows treat the marker as if it were a real credential string.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5f1aa3603

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +289 to +293
const mergedProviders = await resolveProvidersForMode({
mode,
targetPath,
providers: normalizedProviders,
secretRefManagedProviders,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-normalize providers after merge-mode overlay

ensureOpenClawModelsJson now normalizes providers before calling resolveProvidersForMode, but the merge step can re-introduce raw entries from existing models.json and those entries are written without a second normalization pass. In merge mode, providers that exist only in the previous file (for example after being removed from config) can therefore retain unresolved SecretRef objects or template placeholders in persisted auth fields, which defeats the hardening goal and surfaces as unresolved/invalid credentials on later reads and audits.

Useful? React with 👍 / 👎.

@joshavant joshavant force-pushed the feat/secretref-models-persistence-hardening-2 branch from c5f1aa3 to d960bbd Compare March 7, 2026 17:05
@joshavant joshavant force-pushed the feat/secretref-models-persistence-hardening-2 branch from d960bbd to 488b3fa Compare March 7, 2026 17:15
@openclaw-barnacle openclaw-barnacle bot added channel: discord Channel integration: discord channel: telegram Channel integration: telegram labels Mar 7, 2026
@joshavant joshavant merged commit 8e20dd2 into main Mar 7, 2026
31 of 32 checks passed
@joshavant joshavant deleted the feat/secretref-models-persistence-hardening-2 branch March 7, 2026 17:28
vincentkoc added a commit that referenced this pull request Mar 7, 2026
* origin/main: (37 commits)
  chore: land #39056 Node version hint sync (thanks @onstash)
  fix(security): use icacls /sid for locale-independent Windows ACL audit (#38900)
  fix(ci): refresh detect-secrets baseline offsets
  fix(security): strip custom auth headers on cross-origin redirects
  fix(ci): harden diffs viewer request guard and secret scan baseline
  Secrets: harden SecretRef-safe models.json persistence (#38955)
  docs(changelog): credit allowlist scoping report
  refactor(commands): dedupe onboard search perplexity test setup
  refactor(commands): dedupe message command secret-config tests
  refactor(cli): dedupe restart health probe setup tests
  refactor(cron): dedupe interim retry fallback assertions
  refactor(commands): dedupe model probe target test fixtures
  refactor(discord): dedupe message preflight test runners
  refactor(discord): share message handler test scaffolding
  refactor(discord): dedupe native command ACP routing test setup
  refactor(slack): dedupe app mention in-flight race setup
  refactor(slack): reuse shared prepare test scaffolding
  refactor(plugin-sdk): extract shared channel prelude exports
  refactor(slack): dedupe app mention race test setup
  refactor(line): dedupe replay webhook test fixtures
  ...

# Conflicts:
#	.secrets.baseline
#	apps/ios/fastlane/Fastfile
#	src/commands/message.test.ts
mcaxtr pushed a commit to mcaxtr/openclaw that referenced this pull request Mar 7, 2026
vincentkoc pushed a commit to BryanTegomoh/openclaw-fork that referenced this pull request Mar 8, 2026
openperf pushed a commit to openperf/moltbot that referenced this pull request Mar 8, 2026
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
GordonSH-oss pushed a commit to GordonSH-oss/openclaw that referenced this pull request Mar 9, 2026
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 21, 2026
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: discord Channel integration: discord channel: telegram Channel integration: telegram commands Command implementations docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants