Browse Source

fix(config): cover shipped bundle source ownership

Tianyi Cui 1 month ago
parent
commit
4ee93f7944

+ 0 - 1
packages/bundle/base/cordis.patch.yml

@@ -368,7 +368,6 @@
       name: '@deepseek-ai/dsh-web-search-deepseek'
       config:
         apiKeyEnv: DEEPSEEK_API_KEY
-        baseURL: !!js process.env.DEEPSEEK_SEARCH_BASE_URL
 
     - id: tool-web
       name: '@deepseek-ai/dsh-tool-web'

+ 30 - 0
scripts/verify-config-source-ownership.spec.ts

@@ -0,0 +1,30 @@
+import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+import { afterEach, describe, expect, it } from 'vitest'
+import { collectConfigSourceOwnershipViolations } from './verify-config-source-ownership.ts'
+
+const roots: string[] = []
+
+afterEach(() => {
+  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
+})
+
+describe('configuration source ownership gate', () => {
+  it('rejects inline endpoints in shipped bundle patches', () => {
+    const root = mkdtempSync(join(tmpdir(), 'dsh-config-source-ownership-'))
+    roots.push(root)
+    const directory = join(root, 'packages/bundle/base')
+    mkdirSync(directory, { recursive: true })
+    writeFileSync(
+      join(directory, 'cordis.patch.yml'),
+      'config:\n  baseURL: !!js process.env.DEEPSEEK_SEARCH_BASE_URL\n',
+    )
+
+    expect(collectConfigSourceOwnershipViolations(root)).toEqual([
+      'packages/bundle/base/cordis.patch.yml:2: inlines a credential or endpoint from the environment.'
+      + ' The adapter resolves apiKeyEnv through ctx.credentials and the endpoint through the'
+      + ' environment snapshot; inlining here bypasses both ladders.',
+    ])
+  })
+})

+ 29 - 22
scripts/verify-config-source-ownership.ts

@@ -16,6 +16,7 @@ const SHIPPED_CONFIG_GLOBS = [
   'apps/*/config/*.yml',
   'examples/*/*.cordis.yml',
   'examples/*/cordis.yml',
+  'packages/bundle/*/cordis.patch.yml',
   // The Python runtime ships its own default composition inside the wheel.
   'python/*/src/**/cordis.yml',
 ]
@@ -29,29 +30,35 @@ const SHIPPED_CONFIG_GLOBS = [
  */
 const INLINE_DENY = /^\s*(apiKey|baseURL|apiKeyEnv|authToken|headers)\s*:\s*!!js\b/
 
-const failures: string[] = []
-
-for (const glob of SHIPPED_CONFIG_GLOBS) {
-  for (const file of globSync(glob, { cwd: ROOT })) {
-    const rel = file.split(sep).join('/')
-    readFileSync(resolve(ROOT, rel), 'utf8').split('\n').forEach((line, index) => {
-      if (!INLINE_DENY.test(line)) return
-      failures.push(
-        `${rel}:${String(index + 1)}: inlines a credential or endpoint from the environment.`
-        + ' The adapter resolves apiKeyEnv through ctx.credentials and the endpoint through the'
-        + ' environment snapshot; inlining here bypasses both ladders.',
-      )
-    })
+/** Return every forbidden inline environment form in shipped configuration. */
+export function collectConfigSourceOwnershipViolations(root: string): string[] {
+  const failures: string[] = []
+  for (const glob of SHIPPED_CONFIG_GLOBS) {
+    for (const file of globSync(glob, { cwd: root })) {
+      const rel = file.split(sep).join('/')
+      readFileSync(resolve(root, rel), 'utf8').split('\n').forEach((line, index) => {
+        if (!INLINE_DENY.test(line)) return
+        failures.push(
+          `${rel}:${String(index + 1)}: inlines a credential or endpoint from the environment.`
+          + ' The adapter resolves apiKeyEnv through ctx.credentials and the endpoint through the'
+          + ' environment snapshot; inlining here bypasses both ladders.',
+        )
+      })
+    }
   }
+  return failures
 }
 
-if (failures.length > 0) {
-  process.stderr.write('verify-config-source-ownership: configuration source ownership violated:\n')
-  for (const failure of failures) process.stderr.write(`  ${failure}\n`)
-  process.exit(1)
-}
+if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
+  const failures = collectConfigSourceOwnershipViolations(ROOT)
+  if (failures.length > 0) {
+    process.stderr.write('verify-config-source-ownership: configuration source ownership violated:\n')
+    for (const failure of failures) process.stderr.write(`  ${failure}\n`)
+    process.exit(1)
+  }
 
-process.stdout.write(
-  'verify-config-source-ownership: no credential or endpoint uses the ordinary inline environment form'
-  + ' in shipped configuration.\n',
-)
+  process.stdout.write(
+    'verify-config-source-ownership: no credential or endpoint uses the ordinary inline environment form'
+    + ' in shipped configuration.\n',
+  )
+}