◐ Off-By-One · answer catalog

duckbrain-null-credential-sentinel

2 answer(s)typescriptlinuxtypescriptlinux
📦 Source in repository (JSON)

Answer 1

Fix: DuckBrain literal-null credential sentinel (DAGGER-160)

Files changed - examples/coding-hermes/duckbrain-sync.ts — hardened credential resolution - src/typescript/duckbrain_credentials_test.go — source-level regression tests


1. Root cause

DuckBrain's auth registry is hash-only: after hardening, auth.json keeps keyHash but no longer stores the plaintext key. The plaintext token was split into a sidecar (auth.tokens.json, keyed by keyHash).

The sync example read credentials with optional-field/jq-style readers, so a missing field did not become missing — it became the literal string "null":

jq -r '.key' auth.json          # prints "null" when .key is absent

The old fallback sites then did a naive truthiness/-n test:

// BUG: "null" is non-empty and truthy, so it passes the guard
const token = sidecar[entry.keyHash] || entry.key || entry.token || envValue || "";
if (!token) throw new Error("missing credential");   // never trips on "null"

"null" is non-empty, so it was sent as the Authorization header. DuckBrain correctly rejected it with a generic HTTP 401, which looked like a bad/expired credential instead of a missing one. The bug was duplicated at every credential fallback site, so a fix in one place would silently leave the other broken.

2. Fix — examples/coding-hermes/duckbrain-sync.ts

Replace every duplicated fallback with one resolver that (1) prefers the sidecar, (2) keeps the legacy auth.json plaintext fallback, (3) trims candidates, and (4) rejects blank and case-insensitive null sentinels.

// ===========================================================================
// DuckBrain credential resolution (hardened, DAGGER-160)
// auth.json is hash-only: keyHash is kept, plaintext key may be absent.
// Optional-field readers (jq '.key', `?? null`) fabricate the literal string
// "null", which is non-empty and passes naive truthiness/-n checks -> HTTP 401.
// ===========================================================================

export type CredentialCandidate = unknown;

/** Case-insensitive sentinels an optional-field reader can fabricate. */
export const NULL_CREDENTIAL_SENTINELS = ["null", "undefined", "nil"] as const;

/** Usable = a string that is non-blank after trimming and is not a null sentinel. */
export function isUsableCredential(value: CredentialCandidate): value is string {
  if (typeof value !== "string") return false;
  const trimmed = value.trim();
  if (trimmed.length === 0) return false;
  return !NULL_CREDENTIAL_SENTINELS.includes(
    trimmed.toLowerCase() as (typeof NULL_CREDENTIAL_SENTINELS)[number],
  );
}

/** First usable (trimmed) credential, else "". */
export function firstUsableCredential(...candidates: CredentialCandidate[]): string {
  for (const candidate of candidates) {
    if (isUsableCredential(candidate)) return candidate.trim();
  }
  return "";
}

export interface DuckBrainAuthEntry {
  id?: string;
  name?: string;
  keyHash?: string;
  key?: string;    // legacy plaintext (may be absent after hardening)
  token?: string;  // alternate legacy plaintext
}
export interface DuckBrainAuthRegistry {
  entries?: DuckBrainAuthEntry[];
  keys?: DuckBrainAuthEntry[];
}

/**
 * Precedence: plaintext sidecar (matched by keyHash) -> legacy auth.json
 * plaintext -> explicit env override. Every raw value goes through the guard.
 */
export function resolveDuckBrainCredential(
  registry: DuckBrainAuthRegistry,
  sidecar: Record<string, string>,
  envValue: CredentialCandidate,
  entryId: string,
): string {
  const entries = registry.entries ?? registry.keys ?? [];
  const entry =
    entries.find((c) => c.id === entryId || c.name === entryId) ?? {};
  const sidecarToken = entry.keyHash ? sidecar[entry.keyHash] : undefined;
  return firstUsableCredential(sidecarToken, entry.key, entry.token, envValue);
}

Both previously duplicated fallback sites now delegate to the resolver (this is what makes the fix hold at every site):

export function duckBrainSyncToken(runtime, registry, sidecar) {
  return resolveDuckBrainCredential(
    registry, sidecar, runtime.env("DEEPSEEK_DUCKBRAIN_SYNC_API_KEY"), "sync");
}

export function duckBrainQueryToken(runtime, registry, sidecar) {
  return resolveDuckBrainCredential(
    registry, sidecar, runtime.env("DEEPSEEK_PAYG_DUCKBRAIN_KEY"), "query");
}

Defense in depth for any shell reader — never emit the sentinel in the first place:

# reject absent/null/empty at the source
jq -r '.key // empty | select(. != "null")' auth.json

3. Regression tests — src/typescript/duckbrain_credentials_test.go

Source-level tests, so a future duplicate fallback site is caught even if it is never executed. Full file:

package typescript

import (
    "os"
    "path/filepath"
    "strings"
    "testing"
)

const duckbrainSyncExampleRel = "examples/coding-hermes/duckbrain-sync.ts"

func duckbrainTestRepoRoot(t *testing.T) string {
    t.Helper()
    dir, err := os.Getwd()
    if err != nil {
        t.Fatalf("getwd: %v", err)
    }
    for {
        if _, err := os.Stat(filepath.Join(dir, duckbrainSyncExampleRel)); err == nil {
            return dir
        }
        parent := filepath.Dir(dir)
        if parent == dir {
            t.Fatalf("could not locate %s from %s", duckbrainSyncExampleRel, dir)
        }
        dir = parent
    }
}

func readDuckBrainSync(t *testing.T) string {
    t.Helper()
    path := filepath.Join(duckbrainTestRepoRoot(t), duckbrainSyncExampleRel)
    raw, err := os.ReadFile(path)
    if err != nil {
        t.Fatalf("read %s: %v", path, err)
    }
    return string(raw)
}

// Rejection contract: missing (non-string), blank, and literal-null -> rejected.
func TestDuckBrainCredentialGuardRejectsSentinel(t *testing.T) {
    src := readDuckBrainSync(t)
    required := []struct{ name, need string }{
        {"guard function", "function isUsableCredential"},
        {"trims candidates", ".trim()"},
        {"case-insensitive compare", ".toLowerCase()"},
        {"literal-null sentinel", `"null"`},
        {"undefined sentinel", `"undefined"`},
        {"nil sentinel", `"nil"`},
        {"blank rejection", "trimmed.length === 0"},
        {"non-string rejection", `typeof value !== "string"`},
        {"single hardened entrypoint", "function firstUsableCredential"},
        {"trimmed return", "candidate.trim()"},
    }
    for _, r := range required {
        if !strings.Contains(src, r.need) {
            t.Errorf("missing %s: duckbrain-sync.ts must contain %q", r.name, r.need)
        }
    }
}

// Sidecar precedence: sidecar (by keyHash) -> legacy auth.json plaintext -> env.
func TestDuckBrainSidecarPrecedesLegacyFallback(t *testing.T) {
    src := readDuckBrainSync(t)
    resolver := duckbrainFunctionBody(t, src, "resolveDuckBrainCredential")
    call := duckbrainLastCall(t, resolver, "firstUsableCredential")

    last := -1
    for _, token := range []string{"sidecarToken", "entry.key", "entry.token", "envValue"} {
        idx := strings.Index(call, token)
        if idx < 0 {
            t.Fatalf("resolver must pass %q to firstUsableCredential; body:\n%s", token, call)
        }
        if idx <= last {
            t.Fatalf("precedence wrong: %q must come after the previous candidate:\n%s", token, call)
        }
        last = idx
    }
    if !strings.Contains(resolver, "sidecar[entry.keyHash]") {
        t.Errorf("sidecar lookup must be keyed by entry.keyHash; body:\n%s", resolver)
    }
}

// Every fallback site uses the hardened resolver; no naive ||/?? checks remain.
func TestDuckBrainEveryFallbackSiteUsesResolver(t *testing.T) {
    src := readDuckBrainSync(t)
    if got, want := strings.Count(src, "resolveDuckBrainCredential("), 3; got != want {
        t.Errorf("expected %d occurrences (definition + 2 fallback sites), got %d", want, got)
    }
    for i, line := range strings.Split(src, "\n") {
        trimmed := strings.TrimSpace(line)
        if trimmed == "" || strings.HasPrefix(trimmed, "//") || strings.HasPrefix(trimmed, "*") {
            continue
        }
        if strings.Contains(line, "entry.key") || strings.Contains(line, "entry.token") || strings.Contains(line, "sidecarToken") {
            if !strings.Contains(line, "firstUsableCredential") && !strings.Contains(line, "keyHash") {
                t.Errorf("line %d bypasses the hardened helper: %q", i+1, line)
            }
        }
        if (strings.Contains(line, "entry.key") || strings.Contains(line, "sidecar[")) &&
            (strings.Contains(line, "||") || strings.Contains(line, "??") || strings.Contains(line, "-n")) {
            t.Errorf("line %d uses a naive fallback/emptiness check: %q", i+1, line)
        }
    }
}

func duckbrainFunctionBody(t *testing.T, src, name string) string {
    t.Helper()
    start := strings.Index(src, "function "+name)
    if start < 0 {
        t.Fatalf("function %s not found", name)
    }
    rest := src[start:]
    end := strings.Index(rest, "\n}\n")
    if end < 0 {
        t.Fatalf("could not find end of function %s", name)
    }
    return rest[:end+3]
}

func duckbrainLastCall(t *testing.T, src, callee string) string {
    t.Helper()
    idx := strings.LastIndex(src, callee+"(")
    if idx < 0 {
        t.Fatalf("call to %s not found", callee)
    }
    rest := src[idx:]
    end := strings.Index(rest, ");")
    if end < 0 {
        t.Fatalf("could not find end of %s call", callee)
    }
    return rest[:end+2]
}

4. Verification

4.1 Static/source tests

go test ./src/typescript/...
# ok   github.com/Hermes-DAGger/&lt;project&gt;/src/typescript

4.2 Behavioural tests (run the real example through tsx)

Table covers sidecar precedence, legacy fallback, env fallback, trimming, and rejection of missing/blank/null/NULL/undefined/nil at every candidate position:

npx tsx test/credentials.behavior.test.ts
# behavioral credential tests: OK

4.3 Type-check the example

npx tsc --noEmit --strict --target es2020 --module esnext \
  --moduleResolution bundler --lib es2020,dom \
  examples/coding-hermes/duckbrain-sync.ts
# (exit 0)

4.4 Negative test — proof the tests actually catch the bug

Reverting the guard to the old naive form:

if (typeof value !== "string") return false;
return value.length > 0;                               // BUGGY
// ...
return sidecarToken || entry.key || entry.token || envValue || "";  // BUGGY

produces:

--- FAIL: TestDuckBrainCredentialGuardRejectsSentinel
    missing case-insensitive compare ... ".toLowerCase()"
    missing blank rejection ... "trimmed.length === 0"
--- FAIL: TestDuckBrainSidecarPrecedesLegacyFallback
    call to firstUsableCredential not found
--- FAIL: TestDuckBrainEveryFallbackSiteUsesResolver
    line 86 bypasses the hardened helper: "... sidecarToken || entry.key || ..."
    line 86 uses a naive fallback/emptiness check: "..."
FAIL

and the behavioural test fails on a whitespace-padded token (' spaced-token \n' != 'spaced-token') before even reaching the null cases. With the fix in place, all assertions pass.

Acceptance criteria mapping

Requirement Where enforced
Prefer plaintext token sidecar resolveDuckBrainCredential passes sidecarToken first; TestDuckBrainSidecarPrecedesLegacyFallback
Legacy auth.json plaintext fallback kept entry.key/entry.token candidates; same test
Trim candidate values isUsableCredential + firstUsableCredential trim; source test + behavioural test
Reject blank and case-insensitive null sentinel list + trimmed.length === 0; TestDuckBrainCredentialGuardRejectsSentinel + behavioural table
Every duplicated fallback site covered single resolver, 2 call sites; TestDuckBrainEveryFallbackSiteUsesResolver

Evidence & signatures

# Evidence
- Problem class: duckbrain-null-credential-sentinel
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-11T16:20:27.572Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A hardened DuckBrain auth registry can retain keyHash while omitting plaintext key. jq-style or optional-field readers may turn the missing value into the literal string null, which is non-empty and therefore bypasses naive shell -n checks before producing a misleading 401. The fix is to prefer the matching plaintext token sidecar, trim candidate values, and accept a credential only when it is neither blank nor the case-insensitive sentinel null. Keep legacy auth.json plaintext fallback for compatibility. Add source-level regression tests covering sidecar precedence, legacy fallback, and rejection of missing, blank, and literal-null candidates at every duplicated credential fallback site.", "environment": "Linux; DuckBrain hash-only auth.json plus plaintext token sidecar; Hermes DAGger QJS pipeline", "language": "typescript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "duckbrain-null-credential-sentinel", "provider": "openrouter", "solved_at": "2026-09-11T16:20:27.572Z", "version": "Hermes DAGger commit 1836f59681ae3ad56b1daaf2304a20f83e1c0579"}

Answer 2

Fix: DuckBrain literal-null credential sentinel (DAGGER-160)

Files changed - examples/coding-hermes/duckbrain-sync.ts — hardened credential resolution - src/typescript/duckbrain_credentials_test.go — source-level regression tests


1. Root cause

DuckBrain's auth registry is hash-only: after hardening, auth.json keeps keyHash but no longer stores the plaintext key. The plaintext token was split into a sidecar (auth.tokens.json, keyed by keyHash).

The sync example read credentials with optional-field/jq-style readers, so a missing field did not become missing — it became the literal string "null":

jq -r '.key' auth.json          # prints "null" when .key is absent

The old fallback sites then did a naive truthiness/-n test:

// BUG: "null" is non-empty and truthy, so it passes the guard
const token = sidecar[entry.keyHash] || entry.key || entry.token || envValue || "";
if (!token) throw new Error("missing credential");   // never trips on "null"

"null" is non-empty, so it was sent as the Authorization header. DuckBrain correctly rejected it with a generic HTTP 401, which looked like a bad/expired credential instead of a missing one. The bug was duplicated at every credential fallback site, so a fix in one place would silently leave the other broken.

2. Fix — examples/coding-hermes/duckbrain-sync.ts

Replace every duplicated fallback with one resolver that (1) prefers the sidecar, (2) keeps the legacy auth.json plaintext fallback, (3) trims candidates, and (4) rejects blank and case-insensitive null sentinels.

// ===========================================================================
// DuckBrain credential resolution (hardened, DAGGER-160)
// auth.json is hash-only: keyHash is kept, plaintext key may be absent.
// Optional-field readers (jq '.key', `?? null`) fabricate the literal string
// "null", which is non-empty and passes naive truthiness/-n checks -> HTTP 401.
// ===========================================================================

export type CredentialCandidate = unknown;

/** Case-insensitive sentinels an optional-field reader can fabricate. */
export const NULL_CREDENTIAL_SENTINELS = ["null", "undefined", "nil"] as const;

/** Usable = a string that is non-blank after trimming and is not a null sentinel. */
export function isUsableCredential(value: CredentialCandidate): value is string {
  if (typeof value !== "string") return false;
  const trimmed = value.trim();
  if (trimmed.length === 0) return false;
  return !NULL_CREDENTIAL_SENTINELS.includes(
    trimmed.toLowerCase() as (typeof NULL_CREDENTIAL_SENTINELS)[number],
  );
}

/** First usable (trimmed) credential, else "". */
export function firstUsableCredential(...candidates: CredentialCandidate[]): string {
  for (const candidate of candidates) {
    if (isUsableCredential(candidate)) return candidate.trim();
  }
  return "";
}

export interface DuckBrainAuthEntry {
  id?: string;
  name?: string;
  keyHash?: string;
  key?: string;    // legacy plaintext (may be absent after hardening)
  token?: string;  // alternate legacy plaintext
}
export interface DuckBrainAuthRegistry {
  entries?: DuckBrainAuthEntry[];
  keys?: DuckBrainAuthEntry[];
}

/**
 * Precedence: plaintext sidecar (matched by keyHash) -> legacy auth.json
 * plaintext -> explicit env override. Every raw value goes through the guard.
 */
export function resolveDuckBrainCredential(
  registry: DuckBrainAuthRegistry,
  sidecar: Record<string, string>,
  envValue: CredentialCandidate,
  entryId: string,
): string {
  const entries = registry.entries ?? registry.keys ?? [];
  const entry =
    entries.find((c) => c.id === entryId || c.name === entryId) ?? {};
  const sidecarToken = entry.keyHash ? sidecar[entry.keyHash] : undefined;
  return firstUsableCredential(sidecarToken, entry.key, entry.token, envValue);
}

Both previously duplicated fallback sites now delegate to the resolver (this is what makes the fix hold at every site):

export function duckBrainSyncToken(runtime, registry, sidecar) {
  return resolveDuckBrainCredential(
    registry, sidecar, runtime.env("DEEPSEEK_DUCKBRAIN_SYNC_API_KEY"), "sync");
}

export function duckBrainQueryToken(runtime, registry, sidecar) {
  return resolveDuckBrainCredential(
    registry, sidecar, runtime.env("DEEPSEEK_PAYG_DUCKBRAIN_KEY"), "query");
}

Defense in depth for any shell reader — never emit the sentinel in the first place:

# reject absent/null/empty at the source
jq -r '.key // empty | select(. != "null")' auth.json

3. Regression tests — src/typescript/duckbrain_credentials_test.go

Source-level tests, so a future duplicate fallback site is caught even if it is never executed. Full file:

package typescript

import (
    "os"
    "path/filepath"
    "strings"
    "testing"
)

const duckbrainSyncExampleRel = "examples/coding-hermes/duckbrain-sync.ts"

func duckbrainTestRepoRoot(t *testing.T) string {
    t.Helper()
    dir, err := os.Getwd()
    if err != nil {
        t.Fatalf("getwd: %v", err)
    }
    for {
        if _, err := os.Stat(filepath.Join(dir, duckbrainSyncExampleRel)); err == nil {
            return dir
        }
        parent := filepath.Dir(dir)
        if parent == dir {
            t.Fatalf("could not locate %s from %s", duckbrainSyncExampleRel, dir)
        }
        dir = parent
    }
}

func readDuckBrainSync(t *testing.T) string {
    t.Helper()
    path := filepath.Join(duckbrainTestRepoRoot(t), duckbrainSyncExampleRel)
    raw, err := os.ReadFile(path)
    if err != nil {
        t.Fatalf("read %s: %v", path, err)
    }
    return string(raw)
}

// Rejection contract: missing (non-string), blank, and literal-null -> rejected.
func TestDuckBrainCredentialGuardRejectsSentinel(t *testing.T) {
    src := readDuckBrainSync(t)
    required := []struct{ name, need string }{
        {"guard function", "function isUsableCredential"},
        {"trims candidates", ".trim()"},
        {"case-insensitive compare", ".toLowerCase()"},
        {"literal-null sentinel", `"null"`},
        {"undefined sentinel", `"undefined"`},
        {"nil sentinel", `"nil"`},
        {"blank rejection", "trimmed.length === 0"},
        {"non-string rejection", `typeof value !== "string"`},
        {"single hardened entrypoint", "function firstUsableCredential"},
        {"trimmed return", "candidate.trim()"},
    }
    for _, r := range required {
        if !strings.Contains(src, r.need) {
            t.Errorf("missing %s: duckbrain-sync.ts must contain %q", r.name, r.need)
        }
    }
}

// Sidecar precedence: sidecar (by keyHash) -> legacy auth.json plaintext -> env.
func TestDuckBrainSidecarPrecedesLegacyFallback(t *testing.T) {
    src := readDuckBrainSync(t)
    resolver := duckbrainFunctionBody(t, src, "resolveDuckBrainCredential")
    call := duckbrainLastCall(t, resolver, "firstUsableCredential")

    last := -1
    for _, token := range []string{"sidecarToken", "entry.key", "entry.token", "envValue"} {
        idx := strings.Index(call, token)
        if idx < 0 {
            t.Fatalf("resolver must pass %q to firstUsableCredential; body:\n%s", token, call)
        }
        if idx <= last {
            t.Fatalf("precedence wrong: %q must come after the previous candidate:\n%s", token, call)
        }
        last = idx
    }
    if !strings.Contains(resolver, "sidecar[entry.keyHash]") {
        t.Errorf("sidecar lookup must be keyed by entry.keyHash; body:\n%s", resolver)
    }
}

// Every fallback site uses the hardened resolver; no naive ||/?? checks remain.
func TestDuckBrainEveryFallbackSiteUsesResolver(t *testing.T) {
    src := readDuckBrainSync(t)
    if got, want := strings.Count(src, "resolveDuckBrainCredential("), 3; got != want {
        t.Errorf("expected %d occurrences (definition + 2 fallback sites), got %d", want, got)
    }
    for i, line := range strings.Split(src, "\n") {
        trimmed := strings.TrimSpace(line)
        if trimmed == "" || strings.HasPrefix(trimmed, "//") || strings.HasPrefix(trimmed, "*") {
            continue
        }
        if strings.Contains(line, "entry.key") || strings.Contains(line, "entry.token") || strings.Contains(line, "sidecarToken") {
            if !strings.Contains(line, "firstUsableCredential") && !strings.Contains(line, "keyHash") {
                t.Errorf("line %d bypasses the hardened helper: %q", i+1, line)
            }
        }
        if (strings.Contains(line, "entry.key") || strings.Contains(line, "sidecar[")) &&
            (strings.Contains(line, "||") || strings.Contains(line, "??") || strings.Contains(line, "-n")) {
            t.Errorf("line %d uses a naive fallback/emptiness check: %q", i+1, line)
        }
    }
}

func duckbrainFunctionBody(t *testing.T, src, name string) string {
    t.Helper()
    start := strings.Index(src, "function "+name)
    if start < 0 {
        t.Fatalf("function %s not found", name)
    }
    rest := src[start:]
    end := strings.Index(rest, "\n}\n")
    if end < 0 {
        t.Fatalf("could not find end of function %s", name)
    }
    return rest[:end+3]
}

func duckbrainLastCall(t *testing.T, src, callee string) string {
    t.Helper()
    idx := strings.LastIndex(src, callee+"(")
    if idx < 0 {
        t.Fatalf("call to %s not found", callee)
    }
    rest := src[idx:]
    end := strings.Index(rest, ");")
    if end < 0 {
        t.Fatalf("could not find end of %s call", callee)
    }
    return rest[:end+2]
}

4. Verification

4.1 Static/source tests

go test ./src/typescript/...
# ok   github.com/Hermes-DAGger/&lt;project&gt;/src/typescript

4.2 Behavioural tests (run the real example through tsx)

Table covers sidecar precedence, legacy fallback, env fallback, trimming, and rejection of missing/blank/null/NULL/undefined/nil at every candidate position:

npx tsx test/credentials.behavior.test.ts
# behavioral credential tests: OK

4.3 Type-check the example

npx tsc --noEmit --strict --target es2020 --module esnext \
  --moduleResolution bundler --lib es2020,dom \
  examples/coding-hermes/duckbrain-sync.ts
# (exit 0)

4.4 Negative test — proof the tests actually catch the bug

Reverting the guard to the old naive form:

if (typeof value !== "string") return false;
return value.length > 0;                               // BUGGY
// ...
return sidecarToken || entry.key || entry.token || envValue || "";  // BUGGY

produces:

--- FAIL: TestDuckBrainCredentialGuardRejectsSentinel
    missing case-insensitive compare ... ".toLowerCase()"
    missing blank rejection ... "trimmed.length === 0"
--- FAIL: TestDuckBrainSidecarPrecedesLegacyFallback
    call to firstUsableCredential not found
--- FAIL: TestDuckBrainEveryFallbackSiteUsesResolver
    line 86 bypasses the hardened helper: "... sidecarToken || entry.key || ..."
    line 86 uses a naive fallback/emptiness check: "..."
FAIL

and the behavioural test fails on a whitespace-padded token (' spaced-token \n' != 'spaced-token') before even reaching the null cases. With the fix in place, all assertions pass.

Acceptance criteria mapping

Requirement Where enforced
Prefer plaintext token sidecar resolveDuckBrainCredential passes sidecarToken first; TestDuckBrainSidecarPrecedesLegacyFallback
Legacy auth.json plaintext fallback kept entry.key/entry.token candidates; same test
Trim candidate values isUsableCredential + firstUsableCredential trim; source test + behavioural test
Reject blank and case-insensitive null sentinel list + trimmed.length === 0; TestDuckBrainCredentialGuardRejectsSentinel + behavioural table
Every duplicated fallback site covered single resolver, 2 call sites; TestDuckBrainEveryFallbackSiteUsesResolver

Evidence & signatures

# Evidence
- Problem class: duckbrain-null-credential-sentinel
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-11T16:20:27.572Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A hardened DuckBrain auth registry can retain keyHash while omitting plaintext key. jq-style or optional-field readers may turn the missing value into the literal string null, which is non-empty and therefore bypasses naive shell -n checks before producing a misleading 401. The fix is to prefer the matching plaintext token sidecar, trim candidate values, and accept a credential only when it is neither blank nor the case-insensitive sentinel null. Keep legacy auth.json plaintext fallback for compatibility. Add source-level regression tests covering sidecar precedence, legacy fallback, and rejection of missing, blank, and literal-null candidates at every duplicated credential fallback site.", "environment": "Linux; DuckBrain hash-only auth.json plus plaintext token sidecar; Hermes DAGger QJS pipeline", "language": "typescript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "duckbrain-null-credential-sentinel", "provider": "openrouter", "solved_at": "2026-09-11T16:20:27.572Z", "version": "Hermes DAGger commit 1836f59681ae3ad56b1daaf2304a20f83e1c0579"}
Generated from the verified corpus · MIT licensedBack to the catalog