@yansigit/opencodex 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +14 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +225 -0
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +403 -0
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +586 -0
- package/src/server/management/model-routes.ts +560 -0
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const OUTBOUND_PROXY_ENV_KEYS = ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"] as const;
|
|
2
|
+
export const PROXY_ENV_KEYS = [...OUTBOUND_PROXY_ENV_KEYS, "NO_PROXY"] as const;
|
|
3
|
+
|
|
4
|
+
export type ProxyEnvKey = typeof PROXY_ENV_KEYS[number];
|
|
5
|
+
export type ProxyEnvMap = Record<string, string | undefined>;
|
|
6
|
+
|
|
7
|
+
export function proxyEnvPresent(
|
|
8
|
+
key: ProxyEnvKey,
|
|
9
|
+
env: ProxyEnvMap = process.env,
|
|
10
|
+
): boolean {
|
|
11
|
+
return Boolean(env[key]?.trim() || env[key.toLowerCase()]?.trim());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function outboundProxyConfigured(
|
|
15
|
+
env: ProxyEnvMap = process.env,
|
|
16
|
+
): boolean {
|
|
17
|
+
return OUTBOUND_PROXY_ENV_KEYS.some(key => proxyEnvPresent(key, env));
|
|
18
|
+
}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
export const REDACTED_SECRET = "[REDACTED]";
|
|
2
|
+
|
|
3
|
+
const SENSITIVE_KEY_PATTERN = /^(?:authorization|proxy-authorization|cookie|set-cookie|set-cookie2|api[-_]?key|x-api-key|x-goog-api-key|x-amz-security-token|access[-_]?token|refresh[-_]?token|id[-_]?token|token|secret|client[-_]?secret|password|profile[-_]?arn|exa[-_]?api[-_]?key)$/i;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Colon-labelled credential headers echoed back inside an error body
|
|
7
|
+
* (`x-api-key: <value>`), which the `key=value` rules never match.
|
|
8
|
+
*
|
|
9
|
+
* This is one pass with an explicit decision rather than a stack of regexes
|
|
10
|
+
* that have to reason about each other's output. Three earlier attempts failed
|
|
11
|
+
* exactly there: exempting `Bearer` let anything the Bearer rule could not
|
|
12
|
+
* parse escape both rules; trusting the public `[REDACTED]` marker let a
|
|
13
|
+
* suffix ride along behind it; and splitting into two ordered patterns had the
|
|
14
|
+
* second eat the first one's result.
|
|
15
|
+
*
|
|
16
|
+
* The rule: the value after the label is a credential and gets masked to
|
|
17
|
+
* end-of-line. There is no "keep the readable part" exception, because every
|
|
18
|
+
* round of review found another way to hide a credential inside whatever the
|
|
19
|
+
* previous round chose to preserve — a second label, a repeated `Bearer`
|
|
20
|
+
* scheme, a third token two levels deep. Preserving attacker-controlled text
|
|
21
|
+
* next to a credential is the bug; the scheme word is not worth it.
|
|
22
|
+
*
|
|
23
|
+
* `Bearer` survives only as a fixed prefix on `authorization` /
|
|
24
|
+
* `proxy-authorization`, where it says which scheme failed and carries nothing
|
|
25
|
+
* from the input. `[REDACTED]` is a PUBLIC string an upstream can emit too, so
|
|
26
|
+
* its presence never grants trust.
|
|
27
|
+
*
|
|
28
|
+
* The label boundary is matched over a NORMALIZED VIEW: colon confusables and
|
|
29
|
+
* invisible format characters are folded for matching only, with offsets mapped
|
|
30
|
+
* back so unrelated text keeps its original bytes. Folding the string itself
|
|
31
|
+
* rewrote innocent diagnostics (`ratio∶1` became `ratio:1`).
|
|
32
|
+
*/
|
|
33
|
+
// Every letter position also accepts \u0001, the placeholder the fold emits for
|
|
34
|
+
// an unresolved HTML named reference: `authorⅈzation` is the label with one
|
|
35
|
+
// character we cannot name, and that is still the label.
|
|
36
|
+
const CREDENTIAL_HEADER_LABEL_RAW = "x-api-key|x-goog-api-key|x-amz-security-token|api[_-]?key|apiKey|exa[_-]?api[_-]?key|exaApiKey|access[_-]?token|accessToken|refresh[_-]?token|refreshToken|id[_-]?token|client[_-]?secret|clientSecret|authorization|proxy-authorization|cookie|set-cookie|password|secret|token";
|
|
37
|
+
|
|
38
|
+
const CREDENTIAL_HEADER_LABEL = CREDENTIAL_HEADER_LABEL_RAW
|
|
39
|
+
.replace(/(?<![\[\\])([A-Za-z])(?![\]\-])/g, "[$1\u0001]");
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Characters that render as a colon separator. Folded to `:` in the matching
|
|
43
|
+
* view so a look-alike cannot hide a header from the label pattern.
|
|
44
|
+
*/
|
|
45
|
+
const COLON_CONFUSABLES = new Set([
|
|
46
|
+
"\uFF1A", "\uFE55", "\uFE13", "\uA789", "\u02D0", "\u2236",
|
|
47
|
+
"\u205A", "\u0589", "\u1361", "\u16EC", "\u1803", "\u2982", "\u2AF6", "\uFE30",
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Characters dropped from the matching view: anything with no visible width
|
|
52
|
+
* that could split a label into pieces the pattern no longer recognizes.
|
|
53
|
+
* `\p{Default_Ignorable_Code_Point}` is the systematic answer — it covers the
|
|
54
|
+
* zero-width set, the bidi isolates and marks, the Mongolian vowel separator,
|
|
55
|
+
* and the variation selectors in one property instead of a list that review
|
|
56
|
+
* keeps finding another member of. `\p{Cf}` and combining marks are folded too.
|
|
57
|
+
*/
|
|
58
|
+
const INVISIBLE_FORMAT = /[\p{Default_Ignorable_Code_Point}\p{Cf}\p{Mn}\p{Me}]/u;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* HTML named character references.
|
|
62
|
+
*
|
|
63
|
+
* A hand-picked list is a coverage promise nobody can keep — review found
|
|
64
|
+
* `ⅈ`, `ⅇ`, and `ⅆ` decoding to compatibility letters that
|
|
65
|
+
* NFKD already maps onto `i`, `e`, and `d`, and the WHATWG table holds roughly
|
|
66
|
+
* 2200 entries. Neither Bun nor Node exposes that table, and pulling in a
|
|
67
|
+
* dependency to spell a header name is the wrong trade for this path.
|
|
68
|
+
*
|
|
69
|
+
* So names are not resolved at all. A named reference sitting inside a
|
|
70
|
+
* credential label is folded to a single placeholder character of unknown
|
|
71
|
+
* identity, and the label alternation accepts that placeholder wherever a
|
|
72
|
+
* letter may appear. Every named entity is covered, present and future,
|
|
73
|
+
* without claiming to know what any of them mean.
|
|
74
|
+
*/
|
|
75
|
+
const NAMED_ENTITY_PLACEHOLDER = "\u0001";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The handful of named references that spell a SEPARATOR rather than a letter.
|
|
79
|
+
* These have to resolve exactly, because the placeholder stands in for a letter
|
|
80
|
+
* position and a separator is structure, not a character of the name.
|
|
81
|
+
*/
|
|
82
|
+
const SEPARATOR_ENTITIES = new Map<string, string>([
|
|
83
|
+
["colon", ":"], ["semi", ";"], ["equals", "="], ["quot", '"'], ["apos", "'"],
|
|
84
|
+
["lt", "<"], ["gt", ">"], ["amp", "&"], ["sol", "/"], ["lowbar", "_"],
|
|
85
|
+
["hyphen", "-"], ["dash", "-"], ["ndash", "-"], ["mdash", "-"], ["minus", "-"],
|
|
86
|
+
["period", "."], ["comma", ","], ["num", "#"], ["nbsp", " "],
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Latin look-alikes for the ASCII letters that appear in credential labels.
|
|
91
|
+
* Cyrillic `а`/`е`, Greek `ο`, fullwidth forms and the mathematical alphabets
|
|
92
|
+
* all render as the label to a human, so the matching view folds them back.
|
|
93
|
+
* NFKD handles the width/font variants; this table covers the cross-script
|
|
94
|
+
* homoglyphs NFKD deliberately leaves alone.
|
|
95
|
+
*/
|
|
96
|
+
const LETTER_CONFUSABLES = new Map<string, string>([
|
|
97
|
+
// Cyrillic
|
|
98
|
+
["\u0430", "a"], ["\u0435", "e"], ["\u043E", "o"], ["\u0440", "p"], ["\u0441", "c"],
|
|
99
|
+
["\u0445", "x"], ["\u0443", "y"], ["\u04BB", "h"], ["\u0455", "s"], ["\u0456", "i"],
|
|
100
|
+
["\u0458", "j"], ["\u043A", "k"], ["\u0442", "t"], ["\u0432", "b"], ["\u043C", "m"],
|
|
101
|
+
["\u043D", "h"], ["\u0501", "d"], ["\u0503", "g"], ["\u051B", "q"], ["\u051D", "w"],
|
|
102
|
+
["\u04CF", "l"], ["\u0261", "g"], ["\u04AB", "c"], ["\u04BD", "e"], ["\u0459", "k"],
|
|
103
|
+
// Greek
|
|
104
|
+
["\u03B1", "a"], ["\u03BF", "o"], ["\u03C1", "p"], ["\u03BD", "v"], ["\u03BA", "k"],
|
|
105
|
+
["\u03B5", "e"], ["\u03C4", "t"], ["\u03B9", "i"], ["\u03C5", "u"], ["\u03C7", "x"],
|
|
106
|
+
["\u03B7", "n"], ["\u03BC", "u"], ["\u03C3", "o"], ["\u03B2", "b"], ["\u03B3", "y"],
|
|
107
|
+
// Latin extended / other
|
|
108
|
+
["\u0131", "i"], ["\u0269", "i"], ["\u1D0F", "o"], ["\u0280", "r"], ["\u01BF", "p"],
|
|
109
|
+
["\u0578", "n"], ["\u057D", "u"], ["\u0585", "o"], ["\u0581", "g"], ["\u2044", "/"],
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
// `\b` is the wrong left boundary for a header name: it matches after a `-` or
|
|
113
|
+
// `_`, so `not-authorization:` and `internal_token:` were treated as the
|
|
114
|
+
// credential labels they merely end with. Requiring a non-identifier character
|
|
115
|
+
// (or start of input) keeps the match to whole field names.
|
|
116
|
+
//
|
|
117
|
+
// The optional quotes around the label matter: a serialized headers object
|
|
118
|
+
// (`{"x-api-key":"<secret>"}`) puts a closing quote between the name and the
|
|
119
|
+
// colon, so a bare `label:` pattern never saw it. The pre-existing JSON rules
|
|
120
|
+
// below only listed a few field names and did not share this label grammar,
|
|
121
|
+
// which is how ordinary JSON serialization — no homoglyphs, no attacker
|
|
122
|
+
// alphabet — walked a credential straight through.
|
|
123
|
+
const COLON_LABELLED_CREDENTIAL = new RegExp(
|
|
124
|
+
`(?<![A-Za-z0-9_-])["']?(?:${CREDENTIAL_HEADER_LABEL})["']?[^\\S\\r\\n]*:`,
|
|
125
|
+
"gi",
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Framings other than `label: value` that carry the same credential names.
|
|
130
|
+
*
|
|
131
|
+
* An upstream error body is not always a header dump. It can echo the request
|
|
132
|
+
* as a form-encoded string, an XML element, or a multipart part header, and a
|
|
133
|
+
* colon-only matcher sees none of those. Each entry masks the value with the
|
|
134
|
+
* terminator its own grammar defines, so the surrounding structure survives.
|
|
135
|
+
*/
|
|
136
|
+
const OTHER_FRAMED_CREDENTIALS: Array<[RegExp, string]> = [
|
|
137
|
+
// URL query / form-encoded: `authorization=<value>` up to `&` or `;`.
|
|
138
|
+
// Unconditionally to the separator — a quoted value is NOT allowed to end it
|
|
139
|
+
// early, or `authorization="decoy"<secret>&model=…` leaks the suffix.
|
|
140
|
+
[
|
|
141
|
+
new RegExp(`(?<![A-Za-z0-9_-])(?:${CREDENTIAL_HEADER_LABEL})=[^&;\\r\\n]*`, "gi"),
|
|
142
|
+
"=",
|
|
143
|
+
],
|
|
144
|
+
// XML/HTML. A tag qualifies when its NAME is a credential (optionally
|
|
145
|
+
// namespace-qualified), or when a whole `name`/`key`/`id` attribute names one
|
|
146
|
+
// — `data-name` does not count, or `<field data-name="authorization">` loses
|
|
147
|
+
// harmless status text.
|
|
148
|
+
//
|
|
149
|
+
// Once a tag qualifies, the mask keeps only the tag name and runs to END OF
|
|
150
|
+
// INPUT. Two stopping points were tried and both leaked: the CLOSING TAG
|
|
151
|
+
// (same-name nesting ended the mask at the inner `</authorization>`, and a
|
|
152
|
+
// self-closing tag had none), then END OF LINE (an opening tag may legally
|
|
153
|
+
// span lines, so `<authorization\n value="…">` left the credential on the
|
|
154
|
+
// next line). XML has no line discipline to borrow, so there is no boundary
|
|
155
|
+
// left worth trusting.
|
|
156
|
+
//
|
|
157
|
+
// Whitespace is allowed around an attribute `=`, which XML permits and an
|
|
158
|
+
// echo may well reproduce.
|
|
159
|
+
[
|
|
160
|
+
new RegExp(
|
|
161
|
+
`(<[^\\S\\r\\n]*(?:[A-Za-z_][\\w.-]*:)?(?:${CREDENTIAL_HEADER_LABEL})(?=[\\s/>]))[\\s\\S]*`,
|
|
162
|
+
"gi",
|
|
163
|
+
),
|
|
164
|
+
"element",
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
new RegExp(
|
|
168
|
+
`(<[^\\S\\r\\n]*(?:[A-Za-z_][\\w.-]*:)?[A-Za-z_][\\w:.-]*)(?=[^>]*?(?<![\\w:.-])(?:name|key|id)[^\\S]*=[^\\S]*["']?(?:${CREDENTIAL_HEADER_LABEL})["']?(?=[\\s/>]))[\\s\\S]*`,
|
|
169
|
+
"gi",
|
|
170
|
+
),
|
|
171
|
+
"element",
|
|
172
|
+
],
|
|
173
|
+
// Multipart part: everything from a credential-named part header to the end
|
|
174
|
+
// of the input. Part-based, not line-based — a body can span lines and the
|
|
175
|
+
// blank line is often missing in a malformed echo.
|
|
176
|
+
//
|
|
177
|
+
// It deliberately does NOT stop at the first `--`: the boundary token is
|
|
178
|
+
// attacker-controlled text, so a body line starting `--not-the-boundary`
|
|
179
|
+
// ended the mask and exposed everything after it. Consuming the remainder
|
|
180
|
+
// costs trailing context in one framing and closes the bypass.
|
|
181
|
+
[
|
|
182
|
+
new RegExp(
|
|
183
|
+
`(name=["']?(?:${CREDENTIAL_HEADER_LABEL})["']?[^\\r\\n]*\\r?\\n(?:\\r?\\n)?)([\\s\\S]+)`,
|
|
184
|
+
"gi",
|
|
185
|
+
),
|
|
186
|
+
"multipart",
|
|
187
|
+
],
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* These run over the FOLDED view too, then map back to the original string.
|
|
192
|
+
*
|
|
193
|
+
* Matching the raw text meant a percent-encoded form key (`author%69zation=`)
|
|
194
|
+
* and an XML character reference (`name="authorization"`) were invisible,
|
|
195
|
+
* even though both spell the credential name to anything that parses the body.
|
|
196
|
+
* The fold already decodes those, so the grammars are applied there and the
|
|
197
|
+
* mask is written back at the corresponding original offsets.
|
|
198
|
+
*/
|
|
199
|
+
function maskOtherFramings(value: string): string {
|
|
200
|
+
// Same union rule as the header pass: decoding may add coverage, never
|
|
201
|
+
// remove it.
|
|
202
|
+
return maskOtherFramingsOnce(maskOtherFramingsOnce(value, true), false);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function maskOtherFramingsOnce(value: string, decodeEscapes: boolean): string {
|
|
206
|
+
let current = value;
|
|
207
|
+
for (const [pattern, kind] of OTHER_FRAMED_CREDENTIALS) {
|
|
208
|
+
const { folded, map } = foldForMatching(current, decodeEscapes);
|
|
209
|
+
pattern.lastIndex = 0;
|
|
210
|
+
let out = "";
|
|
211
|
+
let cursor = 0;
|
|
212
|
+
let match: RegExpExecArray | null;
|
|
213
|
+
while ((match = pattern.exec(folded)) !== null) {
|
|
214
|
+
const start = map[match.index] ?? current.length;
|
|
215
|
+
const end = map[match.index + match[0].length] ?? current.length;
|
|
216
|
+
if (start < cursor) continue;
|
|
217
|
+
const head = (() => {
|
|
218
|
+
if (kind === "=") {
|
|
219
|
+
const eq = match[0].indexOf("=");
|
|
220
|
+
const headEnd = map[match.index + eq + 1] ?? end;
|
|
221
|
+
return current.slice(start, headEnd);
|
|
222
|
+
}
|
|
223
|
+
const captured = match[1] ?? "";
|
|
224
|
+
const headEnd = map[match.index + captured.length] ?? end;
|
|
225
|
+
return current.slice(start, headEnd);
|
|
226
|
+
})();
|
|
227
|
+
const body = current.slice(start + head.length, end);
|
|
228
|
+
if (kind === "multipart" && !body.trim()) continue;
|
|
229
|
+
out += current.slice(cursor, start) + head + REDACTED_SECRET;
|
|
230
|
+
cursor = end;
|
|
231
|
+
if (pattern.lastIndex === match.index) pattern.lastIndex += 1;
|
|
232
|
+
}
|
|
233
|
+
current = out + current.slice(cursor);
|
|
234
|
+
}
|
|
235
|
+
return current;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Build a folded copy plus an index map back to the original string, so the
|
|
240
|
+
* match runs on normalized text while the output keeps every byte the match did
|
|
241
|
+
* not cover.
|
|
242
|
+
*/
|
|
243
|
+
function foldForMatching(value: string, decodeEscapes = true): { folded: string; map: number[] } {
|
|
244
|
+
let folded = "";
|
|
245
|
+
const map: number[] = [];
|
|
246
|
+
// Serialization escapes are ALIASES for the label, not decoration: a JSON
|
|
247
|
+
// `\u0069`, a percent-encoded `%69`, and an XML `i` all spell the same
|
|
248
|
+
// field name to whatever parses the body, while spelling something else to a
|
|
249
|
+
// literal matcher. Decode them into the matching view (one folded character
|
|
250
|
+
// per escape, with the whole escape mapped back to its start) so
|
|
251
|
+
// `author\u0069zation`, `author%69zation`, and `authorization` are the
|
|
252
|
+
// label they claim to be.
|
|
253
|
+
const decodeEscape = (at: number): { ch: string; width: number } | null => {
|
|
254
|
+
// JSON `\uXXXX`, INCLUDING a surrogate pair. Decoding the halves
|
|
255
|
+
// independently left `\uD835\uDD69` as two lone surrogates, so the
|
|
256
|
+
// mathematical letter they spell was never normalized as one code point.
|
|
257
|
+
const json = /^\\u([0-9a-fA-F]{4})/.exec(value.slice(at, at + 6));
|
|
258
|
+
if (json) {
|
|
259
|
+
const high = parseInt(json[1]!, 16);
|
|
260
|
+
if (high >= 0xd800 && high <= 0xdbff) {
|
|
261
|
+
const low = /^\\u([0-9a-fA-F]{4})/.exec(value.slice(at + 6, at + 12));
|
|
262
|
+
const lowCode = low ? parseInt(low[1]!, 16) : NaN;
|
|
263
|
+
if (lowCode >= 0xdc00 && lowCode <= 0xdfff) {
|
|
264
|
+
return { ch: String.fromCharCode(high, lowCode), width: 12 };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { ch: String.fromCharCode(high), width: 6 };
|
|
268
|
+
}
|
|
269
|
+
// Percent encoding is UTF-8: consecutive `%XX` bytes form ONE character.
|
|
270
|
+
// Decoding each byte on its own turned `%D0%B5` into two unrelated
|
|
271
|
+
// Latin-1 characters instead of the Cyrillic `е` the fold would have
|
|
272
|
+
// recognized.
|
|
273
|
+
const pct = /^(?:%[0-9a-fA-F]{2})+/.exec(value.slice(at, at + 24));
|
|
274
|
+
if (pct) {
|
|
275
|
+
try {
|
|
276
|
+
const decoded = decodeURIComponent(pct[0]);
|
|
277
|
+
if (decoded.length >= 1) {
|
|
278
|
+
// Consume only the bytes that produced the FIRST character, so the
|
|
279
|
+
// rest of the sequence is decoded on the next iteration.
|
|
280
|
+
const first = String.fromCodePoint(decoded.codePointAt(0)!);
|
|
281
|
+
const bytes = new TextEncoder().encode(first).length;
|
|
282
|
+
return { ch: first, width: bytes * 3 };
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
const single = parseInt(pct[0].slice(1, 3), 16);
|
|
286
|
+
return { ch: String.fromCharCode(single), width: 3 };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const xml = /^&#(x[0-9a-fA-F]{1,6}|[0-9]{1,7});/.exec(value.slice(at, at + 11));
|
|
290
|
+
if (xml) {
|
|
291
|
+
const raw = xml[1]!;
|
|
292
|
+
const code = raw[0] === "x" || raw[0] === "X"
|
|
293
|
+
? parseInt(raw.slice(1), 16)
|
|
294
|
+
: parseInt(raw, 10);
|
|
295
|
+
if (Number.isFinite(code) && code > 0 && code <= 0x10ffff) {
|
|
296
|
+
return { ch: String.fromCodePoint(code), width: xml[0].length };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// HTML named references. `:` and the other separator names are
|
|
300
|
+
// resolved exactly; anything else folds to the opaque placeholder so the
|
|
301
|
+
// label still matches without pretending to know the character.
|
|
302
|
+
const named = /^&([A-Za-z][A-Za-z0-9]{1,31});/.exec(value.slice(at, at + 34));
|
|
303
|
+
if (named) {
|
|
304
|
+
const separator = SEPARATOR_ENTITIES.get(named[1]!.toLowerCase());
|
|
305
|
+
return { ch: separator ?? NAMED_ENTITY_PLACEHOLDER, width: named[0].length };
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
};
|
|
309
|
+
// Iterate by CODE POINT, not UTF-16 code unit: a supplementary character
|
|
310
|
+
// (mathematical letters, variation selectors above the BMP) is two units, so
|
|
311
|
+
// a per-unit loop hands each half to the property tests separately and
|
|
312
|
+
// neither half matches anything. `𝕩-api-key` and a U+E0100 inside a label
|
|
313
|
+
// both walked straight past the fold that way.
|
|
314
|
+
let i = 0;
|
|
315
|
+
while (i < value.length) {
|
|
316
|
+
const escaped = decodeEscapes ? decodeEscape(i) : null;
|
|
317
|
+
const ch = escaped ? escaped.ch : String.fromCodePoint(value.codePointAt(i)!);
|
|
318
|
+
const width = escaped ? escaped.width : ch.length;
|
|
319
|
+
if (INVISIBLE_FORMAT.test(ch)) {
|
|
320
|
+
i += width;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const mapped = COLON_CONFUSABLES.has(ch)
|
|
324
|
+
? ":"
|
|
325
|
+
: LETTER_CONFUSABLES.get(ch.toLowerCase())
|
|
326
|
+
// NFKD collapses fullwidth, circled, and mathematical letter variants
|
|
327
|
+
// onto their ASCII base.
|
|
328
|
+
?? (ch.normalize("NFKD").length === 1 ? ch.normalize("NFKD") : ch);
|
|
329
|
+
// One folded unit per source code point keeps the offset map aligned; a
|
|
330
|
+
// multi-unit fold would desynchronize it, so those keep the original.
|
|
331
|
+
folded += mapped.length === 1 ? mapped : ch;
|
|
332
|
+
// One map entry per EMITTED UTF-16 unit. An escaped supplementary
|
|
333
|
+
// character emits two units, and giving it one entry desynchronized every
|
|
334
|
+
// later offset — the mask then landed mid-token and left part of the
|
|
335
|
+
// credential behind.
|
|
336
|
+
const emittedText = mapped.length === 1 ? mapped : ch;
|
|
337
|
+
for (let k = 0; k < emittedText.length; k += 1) map.push(i);
|
|
338
|
+
i += width;
|
|
339
|
+
}
|
|
340
|
+
map.push(value.length);
|
|
341
|
+
return { folded, map };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Run the header rule over BOTH matching views and take the union.
|
|
346
|
+
*
|
|
347
|
+
* Decoding may only ADD coverage. Applying it unconditionally removed some:
|
|
348
|
+
* `𝕩x-api-key: <secret>` decoded to `𝕩x-api-key:`, which folds to
|
|
349
|
+
* `xx-api-key:` and no longer matches the label boundary — so a decode-only
|
|
350
|
+
* view masked LESS than the plain view did. Running both and masking whatever
|
|
351
|
+
* either one finds makes the direction of the change one-way.
|
|
352
|
+
*/
|
|
353
|
+
function maskCredentialHeaders(value: string): string {
|
|
354
|
+
const decoded = maskCredentialHeadersOnce(value, true);
|
|
355
|
+
return maskCredentialHeadersOnce(decoded, false);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function maskCredentialHeadersOnce(value: string, decodeEscapes: boolean): string {
|
|
359
|
+
const { folded, map } = foldForMatching(value, decodeEscapes);
|
|
360
|
+
COLON_LABELLED_CREDENTIAL.lastIndex = 0;
|
|
361
|
+
let out = "";
|
|
362
|
+
let cursor = 0;
|
|
363
|
+
let match: RegExpExecArray | null;
|
|
364
|
+
while ((match = COLON_LABELLED_CREDENTIAL.exec(folded)) !== null) {
|
|
365
|
+
const start = map[match.index] ?? value.length;
|
|
366
|
+
const afterLabel = map[match.index + match[0].length] ?? value.length;
|
|
367
|
+
if (start < cursor) continue;
|
|
368
|
+
const lineEnd = (() => {
|
|
369
|
+
const nl = value.slice(afterLabel).search(/[\r\n]/);
|
|
370
|
+
return nl === -1 ? value.length : afterLabel + nl;
|
|
371
|
+
})();
|
|
372
|
+
// THE VALUE ALWAYS RUNS TO END-OF-LINE. There is no early termination and
|
|
373
|
+
// no attempt to preserve sibling fields.
|
|
374
|
+
//
|
|
375
|
+
// Three attempts tried to be smarter, and each one leaked: stop at the
|
|
376
|
+
// first closing quote; stop at a closing quote followed by punctuation;
|
|
377
|
+
// stop only when the LABEL was quoted. The third still leaked on an
|
|
378
|
+
// unmatched opening quote (`"x-api-key: "decoy",<secret>`) and on a
|
|
379
|
+
// correctly quoted key whose value quote was a decoy
|
|
380
|
+
// (`{"x-api-key":"decoy"<secret>}`).
|
|
381
|
+
//
|
|
382
|
+
// The pattern is the lesson: any rule that stops early is reading
|
|
383
|
+
// attacker-controlled text to decide where a secret ends, and the attacker
|
|
384
|
+
// gets to write that text. Losing the siblings in a serialized object
|
|
385
|
+
// makes a diagnostic less pretty; stopping early makes it leak. Monotonic
|
|
386
|
+
// and blunt wins.
|
|
387
|
+
const valueEnd = lineEnd;
|
|
388
|
+
const rawValue = value.slice(afterLabel, valueEnd);
|
|
389
|
+
if (!rawValue.trim()) continue;
|
|
390
|
+
// Keep the original separator spacing so a diagnostic still reads as
|
|
391
|
+
// `header: [REDACTED]` rather than `header:[REDACTED]`.
|
|
392
|
+
const gap = /^[^\S\r\n]*/.exec(rawValue)?.[0] ?? "";
|
|
393
|
+
const quote = "";
|
|
394
|
+
// `Bearer` is a fixed prefix, reproduced from a literal — never copied from
|
|
395
|
+
// the input — and only where an auth scheme is meaningful.
|
|
396
|
+
const label = match[0].replace(/[^\S\r\n]*:$/, "").trim();
|
|
397
|
+
const isAuthHeader = /^(?:proxy-)?authorization$/i.test(label);
|
|
398
|
+
const prefix = isAuthHeader && new RegExp(`^[^\\S\\r\\n]*${quote}?Bearer[^\\S\\r\\n]`, "i").test(rawValue)
|
|
399
|
+
? "Bearer "
|
|
400
|
+
: "";
|
|
401
|
+
out += value.slice(cursor, afterLabel) + gap + quote + prefix + REDACTED_SECRET + quote;
|
|
402
|
+
cursor = valueEnd;
|
|
403
|
+
}
|
|
404
|
+
return out + value.slice(cursor);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const SECRET_VALUE_PATTERNS: Array<[RegExp, string]> = [
|
|
408
|
+
// A Bearer token outside a labelled header (prose, JSON fragments, logs).
|
|
409
|
+
// Horizontal whitespace only: `\s+` crossed line boundaries and masked the
|
|
410
|
+
// first word of the NEXT line when a header was quoted with a trailing break.
|
|
411
|
+
[/\b(Bearer)([^\S\r\n]+)[A-Za-z0-9._~+/=-]{8,}\b/gi, `$1$2${REDACTED_SECRET}`],
|
|
412
|
+
[/\b(sk-[A-Za-z0-9][A-Za-z0-9._-]{6,})\b/g, REDACTED_SECRET],
|
|
413
|
+
// GitHub tokens (classic + fine-grained + OAuth/refresh): ghp_/gho_/ghu_/ghs_/ghr_/github_pat_.
|
|
414
|
+
[/\b(gh[pousr]_[A-Za-z0-9_]{8,}|github_pat_[A-Za-z0-9_]{20,})\b/g, REDACTED_SECRET],
|
|
415
|
+
// GitHub Copilot API tokens: semicolon-joined k=v grammar starting with tid=…
|
|
416
|
+
// (e.g. "tid=abc123;exp=1699999999;sku=copilot_pro;…:sig"). Redact the whole token —
|
|
417
|
+
// a Bearer-prefix rule alone leaves the suffix intact.
|
|
418
|
+
[/\btid=[A-Za-z0-9-]+(?:;[A-Za-z0-9_.-]+=[^;\s"']*)+(?::[A-Za-z0-9+/=_-]+)?/g, REDACTED_SECRET],
|
|
419
|
+
[/\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|client[_-]?secret|refreshToken|accessToken|clientSecret|apiKey)=)([^&\s"',;]+)/gi, `$1${REDACTED_SECRET}`],
|
|
420
|
+
[/((?:"(?:api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|client[_-]?secret|refreshToken|accessToken|clientSecret|apiKey)"\s*:\s*"))([^"]+)(")/gi, `$1${REDACTED_SECRET}$3`],
|
|
421
|
+
// Raw JSON "token" field values (Copilot token exchange bodies echo the credential here).
|
|
422
|
+
[/(("token"\s*:\s*"))([^"]+)(")/gi, `$1${REDACTED_SECRET}$4`],
|
|
423
|
+
[/\b(arn:aws:[A-Za-z0-9_-]+:[A-Za-z0-9-]*:\d{12}:[A-Za-z0-9_/:+=,.@-]+)\b/g, REDACTED_SECRET],
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
type HeaderRecord = Record<string, string | string[] | undefined>;
|
|
427
|
+
|
|
428
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
429
|
+
if (value === null || typeof value !== "object") return false;
|
|
430
|
+
const prototype = Object.getPrototypeOf(value);
|
|
431
|
+
return prototype === Object.prototype || prototype === null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function isSensitiveKey(key: string): boolean {
|
|
435
|
+
return SENSITIVE_KEY_PATTERN.test(key);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function redactSecretString(value: string): string {
|
|
439
|
+
let redacted = maskOtherFramings(maskCredentialHeaders(value));
|
|
440
|
+
for (const [pattern, replacement] of SECRET_VALUE_PATTERNS) {
|
|
441
|
+
redacted = redacted.replace(pattern, replacement);
|
|
442
|
+
}
|
|
443
|
+
return redacted;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Shared bounded representation for caller-controlled scalar metadata stored in logs. */
|
|
447
|
+
export function sanitizeLogMetadataString(value: unknown, maxLength = 64): string | undefined {
|
|
448
|
+
if (typeof value !== "string" || !Number.isInteger(maxLength) || maxLength < 1) return undefined;
|
|
449
|
+
// Remove every control/line-separator code point that common terminals and log viewers
|
|
450
|
+
// can render as a record boundary before the value reaches a single-line log field.
|
|
451
|
+
const filtered = value.trim().replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, "");
|
|
452
|
+
if (!filtered) return undefined;
|
|
453
|
+
const redacted = redactSecretString(filtered).trim();
|
|
454
|
+
return redacted ? redacted.slice(0, maxLength) : undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function redactSecrets(value: unknown): unknown {
|
|
458
|
+
if (typeof value === "string") return redactSecretString(value);
|
|
459
|
+
if (Array.isArray(value)) return value.map(item => redactSecrets(item));
|
|
460
|
+
if (value instanceof Date) return value;
|
|
461
|
+
if (!isPlainObject(value)) return value;
|
|
462
|
+
|
|
463
|
+
const result: Record<string, unknown> = {};
|
|
464
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
465
|
+
result[key] = isSensitiveKey(key) ? REDACTED_SECRET : redactSecrets(entryValue);
|
|
466
|
+
}
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export function redactHeaders(headers: Headers | HeaderRecord): Record<string, string> {
|
|
471
|
+
const result: Record<string, string> = {};
|
|
472
|
+
const entries = headers instanceof Headers ? headers.entries() : Object.entries(headers);
|
|
473
|
+
|
|
474
|
+
for (const [rawKey, rawValue] of entries) {
|
|
475
|
+
const key = rawKey.toLowerCase();
|
|
476
|
+
if (rawValue === undefined) continue;
|
|
477
|
+
const value = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
|
|
478
|
+
result[key] = isSensitiveKey(key) ? REDACTED_SECRET : redactSecretString(value);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function redactUrlForLog(url: string): string {
|
|
485
|
+
try {
|
|
486
|
+
const parsed = new URL(url);
|
|
487
|
+
parsed.username = "";
|
|
488
|
+
parsed.password = "";
|
|
489
|
+
parsed.search = "";
|
|
490
|
+
parsed.hash = "";
|
|
491
|
+
return parsed.toString();
|
|
492
|
+
} catch {
|
|
493
|
+
return redactSecretString(url.split("?")[0] ?? url);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const USER_HOME_PATH_PATTERNS: Array<[RegExp, string]> = [
|
|
498
|
+
// Windows: C:\Users\<name>\... -> C:\Users\[USER]\...
|
|
499
|
+
[/([A-Za-z]:\\Users\\)[^\\/]+/gi, "$1[USER]"],
|
|
500
|
+
// POSIX: /Users/<name>/... (macOS) and /home/<name>/... (Linux)
|
|
501
|
+
[/(\/(?:Users|home)\/)[^/]+/gi, "$1[USER]"],
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
// Path segments whose name alone looks sensitive. Masked so a configured path
|
|
505
|
+
// cannot surface a secret-flavored substring in diagnostics or logs.
|
|
506
|
+
const SENSITIVE_SEGMENT_PATTERN = /(^|[\\/])([^\\/]*(?:secret|password|passwd|token|api[-_]?key|apikey|credential|email)[^\\/]*)(?=[\\/]|$)/gi;
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Mask the username segment of an absolute home path so diagnostics can print
|
|
510
|
+
* paths without leaking the OS account name, and mask any path segment whose
|
|
511
|
+
* name looks sensitive (token/secret/password/credential/email/...). Path-focused
|
|
512
|
+
* and secret-safe: also runs {@link redactSecretString} for token-shaped values.
|
|
513
|
+
*/
|
|
514
|
+
export function redactUserPath(path: string): string {
|
|
515
|
+
let masked = path;
|
|
516
|
+
for (const [pattern, replacement] of USER_HOME_PATH_PATTERNS) {
|
|
517
|
+
masked = masked.replace(pattern, replacement);
|
|
518
|
+
}
|
|
519
|
+
masked = masked.replace(SENSITIVE_SEGMENT_PATTERN, (_m, sep: string) => `${sep}[REDACTED]`);
|
|
520
|
+
return redactSecretString(masked);
|
|
521
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseRetryAfterMs } from "../combos";
|
|
2
|
+
import { classifyError, parseRetryAfterFromMessage } from "./errors";
|
|
3
|
+
|
|
4
|
+
/** Small default when a retryable 429 has no upstream Retry-After (#507). */
|
|
5
|
+
export const DEFAULT_RETRYABLE_429_RETRY_AFTER_SEC = "2";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validate a raw upstream Retry-After for client exposure.
|
|
9
|
+
* Instant-retry `"0"` is kept even though cooldown parsers reject it.
|
|
10
|
+
*/
|
|
11
|
+
export function validateClientRetryAfterHeader(
|
|
12
|
+
value: string | null | undefined,
|
|
13
|
+
now: number,
|
|
14
|
+
): string | undefined {
|
|
15
|
+
const trimmed = value?.trim();
|
|
16
|
+
if (!trimmed || trimmed.length > 128) return undefined;
|
|
17
|
+
// Instant-retry "0" is a valid client directive but rejected by cooldown parsers
|
|
18
|
+
// (parseRetryAfterMs requires seconds > 0). Preserve it for client headers.
|
|
19
|
+
if (trimmed === "0") return trimmed;
|
|
20
|
+
return parseRetryAfterMs(trimmed, now) !== undefined ? trimmed : undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pick a Retry-After value that clients (especially Codex) can back off on.
|
|
25
|
+
*
|
|
26
|
+
* Order: validated upstream header → delay embedded in the error message →
|
|
27
|
+
* small default for retryable rate-limit 429s. Quota-exhausted 429s get no
|
|
28
|
+
* default — hammering a spent quota is not a backoff problem.
|
|
29
|
+
*
|
|
30
|
+
* Pass `includeDefault: false` when the value feeds cooldown/failover metadata:
|
|
31
|
+
* the synthetic client fallback must not shorten combo cooldowns from 60s to 2s.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveClientRetryAfter(opts: {
|
|
34
|
+
status: number;
|
|
35
|
+
message?: string;
|
|
36
|
+
upstreamRetryAfter?: string | null;
|
|
37
|
+
now?: number;
|
|
38
|
+
includeDefault?: boolean;
|
|
39
|
+
}): string | undefined {
|
|
40
|
+
const now = opts.now ?? Date.now();
|
|
41
|
+
const fromHeader = validateClientRetryAfterHeader(opts.upstreamRetryAfter, now);
|
|
42
|
+
if (fromHeader) return fromHeader;
|
|
43
|
+
|
|
44
|
+
const message = opts.message ?? "";
|
|
45
|
+
const fromMessage = parseRetryAfterFromMessage(message);
|
|
46
|
+
if (fromMessage !== undefined) return String(fromMessage);
|
|
47
|
+
|
|
48
|
+
if (opts.includeDefault === false) return undefined;
|
|
49
|
+
if (opts.status !== 429) return undefined;
|
|
50
|
+
const classified = classifyError(429, "rate_limit_error", message || "Too Many Requests");
|
|
51
|
+
if (classified.type === "insufficient_quota" || classified.code === "insufficient_quota") {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return DEFAULT_RETRYABLE_429_RETRY_AFTER_SEC;
|
|
55
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface SelfLaunchArgvOptions {
|
|
2
|
+
isStandaloneExecutable?: boolean;
|
|
3
|
+
sourceEntrypoint?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** Build argv for re-entering the current CLI in compiled or source mode. */
|
|
7
|
+
export function selfLaunchArgv(
|
|
8
|
+
args: readonly string[],
|
|
9
|
+
options: SelfLaunchArgvOptions = {},
|
|
10
|
+
): string[] {
|
|
11
|
+
const bunStandalone = (Bun as unknown as { isStandaloneExecutable?: boolean }).isStandaloneExecutable;
|
|
12
|
+
const isStandaloneExecutable = options.isStandaloneExecutable ?? Boolean(bunStandalone);
|
|
13
|
+
if (isStandaloneExecutable) return [...args];
|
|
14
|
+
return [options.sourceEntrypoint ?? process.argv[1], ...args];
|
|
15
|
+
}
|