openclaw - ✅(Solved) Fix Expose knownLongFlags in safeBinProfiles config schema [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

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#56805Fetched 2026-04-08 01:47:35
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×1referenced ×1

The safeBinProfiles config schema does not expose the knownLongFlags field, even though it is used internally by validateSafeBinArgv for GNU long-option abbreviation resolution. Attempting to set knownLongFlags in a profile causes a Zod .strict() validation crash: "Unrecognized key(s) in object: 'knownLongFlags'".

This means operators cannot declare long flags as "known" without also categorizing them as allowedValueFlags or deniedFlags, which have different semantics.

Root Cause

In src/config/zod-schema.agent-runtime.ts, ToolExecSafeBinProfileSchema uses .strict() and only defines four fields:

const ToolExecSafeBinProfileSchema = z
  .object({
    minPositional: z.number().int().nonnegative().optional(),
    maxPositional: z.number().int().nonnegative().optional(),
    allowedValueFlags: z.array(z.string()).optional(),
    deniedFlags: z.array(z.string()).optional(),
  })
  .strict();

The knownLongFlags field exists in the internal SafeBinProfile type (src/infra/exec-safe-bin-policy-profiles.ts:7) and is used by collectPositionalTokens() in exec-safe-bin-policy-validator.ts (line 140-143), but it is not present in:

  1. The Zod schema (ToolExecSafeBinProfileSchema) — causes the parse crash
  2. The SafeBinProfileFixture type — no way to pass it through config normalization

Currently, knownLongFlags is auto-derived from allowedValueFlags ∪ deniedFlags (collectKnownLongFlags()). For an empty profile {}, both sets are empty, so knownLongFlags = [] — meaning all long flags are treated as unknown and rejected by consumeLongOptionToken().

Fix Action

Fix / Workaround

Operators running headless/containerized deployments who add custom binaries to safeBins with empty profiles {} cannot make any long flags pass the third validation gate. The only workaround is to list every possible long flag in either allowedValueFlags or deniedFlags, which is impractical and changes their allow/deny semantics.

PR fix notes

PR #56806: feat(exec): expose knownLongFlags in safeBinProfiles config schema

Description (problem / solution / changelog)

Summary

Expose the knownLongFlags field in the safeBinProfiles config schema, allowing operators to declare long flags as "known" for GNU abbreviation resolution without misusing allowedValueFlags or deniedFlags.

Closes #56805

Problem

The knownLongFlags field exists internally in SafeBinProfile and is used by validateSafeBinArgv for GNU long-option abbreviation resolution. However, it is not present in:

  1. The Zod schema (ToolExecSafeBinProfileSchema) — .strict() causes an "Unrecognized key" crash
  2. The SafeBinProfileFixture type — no way to pass it through config normalization

For empty profiles {}, knownLongFlags auto-derives as [], causing all long flags to be treated as unknown and rejected. The only workaround is to list every possible long flag in allowedValueFlags or deniedFlags, which changes their semantics.

Changes

FileChange
src/config/zod-schema.agent-runtime.tsAdd knownLongFlags: z.array(z.string()).optional() to ToolExecSafeBinProfileSchema
src/infra/exec-safe-bin-policy-profiles.tsAdd knownLongFlags to SafeBinProfileFixture type; update normalizeSafeBinProfileFixture and compileSafeBinProfile to normalize and merge with auto-derived flags
src/config/normalize-exec-safe-bin.tsAdd knownLongFlags to the type cast in normalization
src/config/schema.base.generated.tsRegenerated via generate-base-config-schema.ts
src/infra/exec-safe-bin-policy.test.ts6 new tests for the config path

Design Decisions

  • Merge, don't replace: User-provided knownLongFlags are merged with auto-derived flags from allowedValueFlags ∪ deniedFlags, ensuring flags in those lists are always "known" even if not explicitly listed
  • Reuses normalizeFixtureFlags: Same normalization (trim, dedupe, sort) applied to knownLongFlags as to other flag arrays
  • Backwards-compatible: Existing configs without knownLongFlags work identically via auto-derivation

Usage

{
  "tools": {
    "exec": {
      "safeBins": ["mycli"],
      "safeBinProfiles": {
        "mycli": {
          "knownLongFlags": ["--verbose", "--version", "--help"]
        }
      }
    }
  }
}

Test Plan

  • All 96 tests in exec-safe-bin-policy.test.ts pass (90 existing + 6 new)
  • All 90 tests across related test files pass (exec-approvals-safe-bins, exec-safe-bin-runtime-policy, io.compat, doctor/exec-safe-bins, doctor-config-flow.safe-bins, pi-tools.safe-bins)
  • Generated schema test passes
  • Pre-commit hooks pass (oxlint, type checking, conflict markers)

Changed files

  • src/config/normalize-exec-safe-bin.ts (modified, +1/-0)
  • src/config/schema.base.generated.ts (modified, +12/-0)
  • src/config/zod-schema.agent-runtime.ts (modified, +1/-0)
  • src/infra/exec-safe-bin-policy-profiles.ts (modified, +20/-1)
  • src/infra/exec-safe-bin-policy.test.ts (modified, +108/-0)

Code Example

{
  "tools": {
    "exec": {
      "security": "allowlist",
      "safeBins": ["mycli"],
      "safeBinProfiles": {
        "mycli": {
          "knownLongFlags": ["--mode", "--file", "--version"]
        }
      }
    }
  }
}

---

const ToolExecSafeBinProfileSchema = z
  .object({
    minPositional: z.number().int().nonnegative().optional(),
    maxPositional: z.number().int().nonnegative().optional(),
    allowedValueFlags: z.array(z.string()).optional(),
    deniedFlags: z.array(z.string()).optional(),
  })
  .strict();
RAW_BUFFERClick to expand / collapse

Summary

The safeBinProfiles config schema does not expose the knownLongFlags field, even though it is used internally by validateSafeBinArgv for GNU long-option abbreviation resolution. Attempting to set knownLongFlags in a profile causes a Zod .strict() validation crash: "Unrecognized key(s) in object: 'knownLongFlags'".

This means operators cannot declare long flags as "known" without also categorizing them as allowedValueFlags or deniedFlags, which have different semantics.

Reproduction

In settings.json or agent config:

{
  "tools": {
    "exec": {
      "security": "allowlist",
      "safeBins": ["mycli"],
      "safeBinProfiles": {
        "mycli": {
          "knownLongFlags": ["--mode", "--file", "--version"]
        }
      }
    }
  }
}

Expected: Profile accepts knownLongFlags and uses them for long-option resolution. Actual: Config parse crash — Unrecognized key(s) in object: 'knownLongFlags'

Root Cause

In src/config/zod-schema.agent-runtime.ts, ToolExecSafeBinProfileSchema uses .strict() and only defines four fields:

const ToolExecSafeBinProfileSchema = z
  .object({
    minPositional: z.number().int().nonnegative().optional(),
    maxPositional: z.number().int().nonnegative().optional(),
    allowedValueFlags: z.array(z.string()).optional(),
    deniedFlags: z.array(z.string()).optional(),
  })
  .strict();

The knownLongFlags field exists in the internal SafeBinProfile type (src/infra/exec-safe-bin-policy-profiles.ts:7) and is used by collectPositionalTokens() in exec-safe-bin-policy-validator.ts (line 140-143), but it is not present in:

  1. The Zod schema (ToolExecSafeBinProfileSchema) — causes the parse crash
  2. The SafeBinProfileFixture type — no way to pass it through config normalization

Currently, knownLongFlags is auto-derived from allowedValueFlags ∪ deniedFlags (collectKnownLongFlags()). For an empty profile {}, both sets are empty, so knownLongFlags = [] — meaning all long flags are treated as unknown and rejected by consumeLongOptionToken().

Impact

Operators running headless/containerized deployments who add custom binaries to safeBins with empty profiles {} cannot make any long flags pass the third validation gate. The only workaround is to list every possible long flag in either allowedValueFlags or deniedFlags, which is impractical and changes their allow/deny semantics.

Proposed Fix

  1. Add knownLongFlags: z.array(z.string()).optional() to ToolExecSafeBinProfileSchema
  2. Add knownLongFlags to the SafeBinProfileFixture type
  3. Update normalizeSafeBinProfileFixture to normalize knownLongFlags via the existing normalizeFixtureFlags helper
  4. Update compileSafeBinProfile to merge user-provided knownLongFlags with the auto-derived set from allowedValueFlags ∪ deniedFlags

This is a backwards-compatible, additive schema change. Existing configs without knownLongFlags continue to work identically via auto-derivation.

Happy to submit a PR for this.

extent analysis

Fix Plan

To resolve the issue, follow these steps:

  • Update the ToolExecSafeBinProfileSchema to include knownLongFlags.
  • Modify the SafeBinProfileFixture type to include knownLongFlags.
  • Update normalizeSafeBinProfileFixture to handle knownLongFlags.
  • Update compileSafeBinProfile to merge user-provided and auto-derived knownLongFlags.

Code Changes

Update ToolExecSafeBinProfileSchema

const ToolExecSafeBinProfileSchema = z
  .object({
    minPositional: z.number().int().nonnegative().optional(),
    maxPositional: z.number().int().nonnegative().optional(),
    allowedValueFlags: z.array(z.string()).optional(),
    deniedFlags: z.array(z.string()).optional(),
    knownLongFlags: z.array(z.string()).optional(), // Add this line
  })
  .strict();

Update SafeBinProfileFixture type

interface SafeBinProfileFixture {
  // ... existing properties
  knownLongFlags?: string[]; // Add this line
}

Update normalizeSafeBinProfileFixture

function normalizeSafeBinProfileFixture(fixture: SafeBinProfileFixture): SafeBinProfile {
  // ... existing normalization logic
  const knownLongFlags = fixture.knownLongFlags ? normalizeFixtureFlags(fixture.knownLongFlags) : [];
  // ... existing normalization logic
}

Update compileSafeBinProfile

function compileSafeBinProfile(profile: SafeBinProfile): CompiledSafeBinProfile {
  // ... existing compilation logic
  const knownLongFlags = [...(profile.knownLongFlags || []), ...(profile.allowedValueFlags || []), ...(profile.deniedFlags || [])];
  // ... existing compilation logic
}

Verification

To verify the fix, create a test configuration with knownLongFlags in the safeBinProfiles section and check that it does not cause a validation crash. Then, test the long-option resolution with the provided knownLongFlags to ensure it works as expected.

Extra Tips

  • Make sure to update the documentation to reflect the changes to the ToolExecSafeBinProfileSchema and SafeBinProfileFixture type.
  • Consider adding tests to cover the new knownLongFlags field and its interaction with the auto-derived set from allowedValueFlags ∪ deniedFlags.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Expose knownLongFlags in safeBinProfiles config schema [1 pull requests, 1 participants]