toby-bridges/api-relay-audit
toby-bridges/api-relay-audit从 DeepSeek Harness 对 AI API 中转站和 LLM 代理运行本地安全审计,生成 Markdown 报告,覆盖提示词注入、模型替换信号、工具调用改写、错误泄漏、流完整性和按 profile 启用的 Web3 风险。
纯 JavaScript 实现的 macOS Keychain 凭据提供器,替换 DeepSeek Harness 的明文 .credentials.yaml 存储,完整支持 refs 和 records,无需原生构建、代码签名或 Xcode。
macOS Keychain credentials provider for DeepSeek Harness (dsh).
The stock provider (dsh-credentials-local) stores secrets in $DSH_HOME/.credentials.yaml with 0600 permissions. As its own README states: that file is protected from other OS users, but not from the model — tool processes (bash, filesystem tools) run as the same user and can read it like any other file.
This provider moves secrets into the macOS login keychain. A file that does not exist cannot be cat-ed; nothing lands in backups, sync folders, or git.
read, cat, grep, tar, node -e …, in-process or subprocess. With this provider, values live in the login keychain./usr/bin/security — the CLI is ACL-trusted and no prompt appears. An agent with an unrestricted shell that knows to ask the keychain can still exfiltrate. Pairing with a sandbox that constrains tool subprocesses narrows this; a hard boundary needs OS-level separation (a signed broker with a private access group, e.g. keyringseam, or a separate OS user).Net: the bar rises from "any read primitive" to "must exec /usr/bin/security as the same user" — a real improvement, not a complete boundary.
ctx.credentials — refs (API keys) and records (structured credentials like client-connection/browser-session grants). Several other providers implement only the refs half, which means DSH's own session records fall back to the plaintext file./usr/bin/security)0.2.0-rc.1)On DSH 0.2 and newer the plugin must be mounted as a package — installed into the profile, or linked into its node_modules — and referenced by package name. A row that points at an absolute index.js path does not work there, for two reasons: the runtime applies its peer-compatibility gate to plugin rows (ranges that do not admit the running version disable the row), and a module loaded from an absolute path gets no host peer resolution (the harness's own packages live inside app.asar, so there is no on-disk copy to resolve).
dsh plugin --profile <profile> add dsh-keychain-credentials
# or from source:
dsh plugin --profile <profile> add github:<you>/dsh-keychain-credentials
Then the bundle's cordis.patch.yml disables the stock provider and mounts this one. If you prefer to wire it by hand, add to your patch layer (~/.dsh/profiles/<profile>/cordis.patch.yml or --patch <file>):
- id: credentials
disabled: true
- insert:
- id: credentials-keychain
name: dsh-keychain-credentials # 包名,不是绝对路径;插件须已装/链接进该 profile
config:
servicePrefix: dsh-credentials # keychain service prefix
account: dsh # keychain account name
Store a secret (account = credential reference name):
security add-generic-password -U -s dsh-credentials -a DEEPSEEK_API_KEY -w 'sk-…'
The LLM adapter resolves the reference per request — no restart needed after rotation.
Semantics kept from the seam contract:
set/unset reject while a read-only source (the environment) shadows the ref.describe checks existence without reading the value — a status query never pulls plaintext into the agent process.security CLI are sanitized: exit code and stderr only, never the command line (which would carry the secret on a failed set).security over stdin, never argv, so they never appear in ps.security -i rather than the stdin password prompt: that prompt stores at most 128 bytes and truncates anything longer silently. Interactive mode has no such limit but reports no failure exit code, so those writes are confirmed by reading the value back. (The account token DSH stores as a records entry is one such value.)npm install # or pnpm install — devDependencies supply the seam packages
npm test
test/test-provider.mjs exercises the real macOS login keychain with throwaway probe entries under the dsh-credentials-test service prefix (it writes, reads back, and deletes them; it never touches a real key). test/test-e2e.mjs checks the peer ranges against the installed harness and, given a profile name (node test/test-e2e.mjs desktop), the profile's mount shape.
MIT