@elie-laloum/outpost 11.1.3 → 11.2.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/CHANGELOG.md +25 -0
- package/README.md +1 -0
- package/SECURITY.md +5 -1
- package/dist/adapters/agents/antigravity/antigravity-adapter.js +9 -0
- package/dist/adapters/agents/antigravity/antigravity-adapter.js.map +1 -1
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.js +39 -0
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js +394 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.d.ts +2 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.js +6 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.d.ts +114 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.js +2 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity.types.d.ts +1 -0
- package/dist/adapters/agents/antigravity/antigravity.types.js.map +1 -1
- package/dist/adapters/agents/authentication.js +1 -0
- package/dist/adapters/agents/authentication.js.map +1 -1
- package/dist/adapters/agents/claude/claude-adapter.js +15 -0
- package/dist/adapters/agents/claude/claude-adapter.js.map +1 -1
- package/dist/adapters/agents/claude/claude-hook-plan.constants.d.ts +1 -0
- package/dist/adapters/agents/claude/claude-hook-plan.constants.js +20 -0
- package/dist/adapters/agents/claude/claude-hook-plan.constants.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hook-plan.d.ts +4 -0
- package/dist/adapters/agents/claude/claude-hook-plan.js +60 -0
- package/dist/adapters/agents/claude/claude-hook-plan.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.js +2968 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.d.ts +2 -0
- package/dist/adapters/agents/claude/claude-hooks.js +6 -0
- package/dist/adapters/agents/claude/claude-hooks.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.types.d.ts +900 -0
- package/dist/adapters/agents/claude/claude-hooks.types.js +2 -0
- package/dist/adapters/agents/claude/claude-hooks.types.js.map +1 -0
- package/dist/adapters/agents/claude/claude-profile.d.ts +1 -0
- package/dist/adapters/agents/claude/claude-profile.js +23 -11
- package/dist/adapters/agents/claude/claude-profile.js.map +1 -1
- package/dist/adapters/agents/codex/codex-adapter.js +17 -1
- package/dist/adapters/agents/codex/codex-adapter.js.map +1 -1
- package/dist/adapters/agents/codex/codex-authentication.js +1 -0
- package/dist/adapters/agents/codex/codex-authentication.js.map +1 -1
- package/dist/adapters/agents/codex/codex-conversations.constants.d.ts +1 -0
- package/dist/adapters/agents/codex/codex-conversations.constants.js +2 -0
- package/dist/adapters/agents/codex/codex-conversations.constants.js.map +1 -0
- package/dist/adapters/agents/codex/codex-conversations.js +8 -5
- package/dist/adapters/agents/codex/codex-conversations.js.map +1 -1
- package/dist/adapters/agents/codex/codex-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/codex/codex-hook-plan.js +72 -0
- package/dist/adapters/agents/codex/codex-hook-plan.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.js +942 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.d.ts +2 -0
- package/dist/adapters/agents/codex/codex-hooks.js +6 -0
- package/dist/adapters/agents/codex/codex-hooks.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.types.d.ts +255 -0
- package/dist/adapters/agents/codex/codex-hooks.types.js +2 -0
- package/dist/adapters/agents/codex/codex-hooks.types.js.map +1 -0
- package/dist/adapters/agents/codex/codex-provider.js +2 -2
- package/dist/adapters/agents/codex/codex-provider.js.map +1 -1
- package/dist/adapters/agents/copilot/copilot-adapter.js +14 -0
- package/dist/adapters/agents/copilot/copilot-adapter.js.map +1 -1
- package/dist/adapters/agents/copilot/copilot-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/copilot/copilot-hook-plan.js +26 -0
- package/dist/adapters/agents/copilot/copilot-hook-plan.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.js +528 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.d.ts +2 -0
- package/dist/adapters/agents/copilot/copilot-hooks.js +6 -0
- package/dist/adapters/agents/copilot/copilot-hooks.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.d.ts +166 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.js +2 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot.types.d.ts +1 -0
- package/dist/adapters/agents/copilot/copilot.types.js.map +1 -1
- package/dist/adapters/agents/hook-groups.d.ts +2 -0
- package/dist/adapters/agents/hook-groups.js +17 -0
- package/dist/adapters/agents/hook-groups.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-adapter.js +16 -1
- package/dist/adapters/agents/kimi/kimi-adapter.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/kimi/kimi-hook-plan.js +33 -0
- package/dist/adapters/agents/kimi/kimi-hook-plan.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.js +747 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.d.ts +2 -0
- package/dist/adapters/agents/kimi/kimi-hooks.js +6 -0
- package/dist/adapters/agents/kimi/kimi-hooks.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.d.ts +226 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.js +2 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-request.js +2 -0
- package/dist/adapters/agents/kimi/kimi-request.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi.constants.d.ts +1 -0
- package/dist/adapters/agents/kimi/kimi.constants.js +1 -0
- package/dist/adapters/agents/kimi/kimi.constants.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi.types.d.ts +1 -0
- package/dist/adapters/agents/kimi/kimi.types.js.map +1 -1
- package/dist/adapters/agents/native-hooks.d.ts +4 -0
- package/dist/adapters/agents/native-hooks.js +63 -0
- package/dist/adapters/agents/native-hooks.js.map +1 -0
- package/dist/adapters/agents/native-hooks.types.d.ts +22 -0
- package/dist/adapters/agents/native-hooks.types.js +2 -0
- package/dist/adapters/agents/native-hooks.types.js.map +1 -0
- package/dist/adapters/agents/settings.types.d.ts +2 -0
- package/dist/adapters/agents/settings.types.js.map +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.d.ts +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.js +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.js.map +1 -1
- package/dist/adapters/decisions/system-one-provider.js +8 -21
- package/dist/adapters/decisions/system-one-provider.js.map +1 -1
- package/dist/adapters/models/anthropic-model-provider.constants.d.ts +1 -0
- package/dist/adapters/models/anthropic-model-provider.constants.js +4 -0
- package/dist/adapters/models/anthropic-model-provider.constants.js.map +1 -1
- package/dist/adapters/models/anthropic-model-provider.js +7 -1
- package/dist/adapters/models/anthropic-model-provider.js.map +1 -1
- package/dist/adapters/models/http-failure.d.ts +3 -0
- package/dist/adapters/models/http-failure.js +43 -0
- package/dist/adapters/models/http-failure.js.map +1 -0
- package/dist/adapters/models/http-provider.js +14 -20
- package/dist/adapters/models/http-provider.js.map +1 -1
- package/dist/adapters/models/model-protocol.types.d.ts +1 -0
- package/dist/adapters/models/model-protocol.types.js.map +1 -1
- package/dist/adapters/models/model.constants.d.ts +2 -0
- package/dist/adapters/models/model.constants.js +17 -0
- package/dist/adapters/models/model.constants.js.map +1 -1
- package/dist/adapters/models/openai-chat-stream.js +3 -1
- package/dist/adapters/models/openai-chat-stream.js.map +1 -1
- package/dist/adapters/models/openai-responses.js +4 -0
- package/dist/adapters/models/openai-responses.js.map +1 -1
- package/dist/adapters/secrets/aws.js +5 -4
- package/dist/adapters/secrets/aws.js.map +1 -1
- package/dist/adapters/secrets/azure.constants.d.ts +1 -0
- package/dist/adapters/secrets/azure.constants.js +2 -0
- package/dist/adapters/secrets/azure.constants.js.map +1 -0
- package/dist/adapters/secrets/azure.js +2 -1
- package/dist/adapters/secrets/azure.js.map +1 -1
- package/dist/adapters/secrets/gcp.constants.d.ts +1 -0
- package/dist/adapters/secrets/gcp.constants.js +2 -0
- package/dist/adapters/secrets/gcp.constants.js.map +1 -0
- package/dist/adapters/secrets/gcp.js +11 -9
- package/dist/adapters/secrets/gcp.js.map +1 -1
- package/dist/adapters/secrets/infisical.js +3 -2
- package/dist/adapters/secrets/infisical.js.map +1 -1
- package/dist/adapters/secrets/onepassword.constants.d.ts +1 -0
- package/dist/adapters/secrets/onepassword.constants.js +2 -0
- package/dist/adapters/secrets/onepassword.constants.js.map +1 -0
- package/dist/adapters/secrets/onepassword.js +2 -1
- package/dist/adapters/secrets/onepassword.js.map +1 -1
- package/dist/adapters/secrets/secret-source.js +3 -1
- package/dist/adapters/secrets/secret-source.js.map +1 -1
- package/dist/adapters/secrets/vault-response.js +5 -5
- package/dist/adapters/secrets/vault-response.js.map +1 -1
- package/dist/adapters/secrets/vault.js +4 -3
- package/dist/adapters/secrets/vault.js.map +1 -1
- package/dist/adapters/testing/scripted-agent.d.ts +3 -1
- package/dist/adapters/testing/scripted-agent.js +3 -1
- package/dist/adapters/testing/scripted-agent.js.map +1 -1
- package/dist/adapters/tools/edit-tools.js +3 -3
- package/dist/adapters/tools/edit-tools.js.map +1 -1
- package/dist/adapters/tools/git-tools.js +16 -1
- package/dist/adapters/tools/git-tools.js.map +1 -1
- package/dist/adapters/tools/mcp-launcher.constants.d.ts +1 -1
- package/dist/adapters/tools/mcp-launcher.constants.js +29 -7
- package/dist/adapters/tools/mcp-launcher.constants.js.map +1 -1
- package/dist/adapters/tools/mcp-resources.js +5 -1
- package/dist/adapters/tools/mcp-resources.js.map +1 -1
- package/dist/adapters/tools/mcp.constants.d.ts +0 -6
- package/dist/adapters/tools/mcp.constants.js +0 -6
- package/dist/adapters/tools/mcp.constants.js.map +1 -1
- package/dist/adapters/tools/mcp.js +1 -0
- package/dist/adapters/tools/mcp.js.map +1 -1
- package/dist/adapters/tools/sandbox-files.constants.d.ts +1 -0
- package/dist/adapters/tools/sandbox-files.constants.js +17 -0
- package/dist/adapters/tools/sandbox-files.constants.js.map +1 -0
- package/dist/adapters/tools/sandbox-files.d.ts +1 -0
- package/dist/adapters/tools/sandbox-files.js +14 -1
- package/dist/adapters/tools/sandbox-files.js.map +1 -1
- package/dist/adapters/tools/tools.constants.js +1 -0
- package/dist/adapters/tools/tools.constants.js.map +1 -1
- package/dist/adapters/triggers/github-webhook.js +14 -11
- package/dist/adapters/triggers/github-webhook.js.map +1 -1
- package/dist/adapters/triggers/gitlab-webhook.js +16 -13
- package/dist/adapters/triggers/gitlab-webhook.js.map +1 -1
- package/dist/adapters/triggers/slack-request.js +10 -7
- package/dist/adapters/triggers/slack-request.js.map +1 -1
- package/dist/adapters/triggers/standard-webhook.js +10 -7
- package/dist/adapters/triggers/standard-webhook.js.map +1 -1
- package/dist/application/agent-authentication.js +4 -1
- package/dist/application/agent-authentication.js.map +1 -1
- package/dist/application/agent-turn.js +20 -5
- package/dist/application/agent-turn.js.map +1 -1
- package/dist/application/cli-hooks.constants.d.ts +2 -0
- package/dist/application/cli-hooks.constants.js +69 -0
- package/dist/application/cli-hooks.constants.js.map +1 -0
- package/dist/application/cli-hooks.d.ts +3 -0
- package/dist/application/cli-hooks.js +168 -0
- package/dist/application/cli-hooks.js.map +1 -0
- package/dist/application/cli-steering.js +33 -2
- package/dist/application/cli-steering.js.map +1 -1
- package/dist/application/custom-turn.js +10 -1
- package/dist/application/custom-turn.js.map +1 -1
- package/dist/application/diagnostic-probe.d.ts +1 -1
- package/dist/application/diagnostic-probe.js +7 -3
- package/dist/application/diagnostic-probe.js.map +1 -1
- package/dist/application/dispatch-observation.js +4 -0
- package/dist/application/dispatch-observation.js.map +1 -1
- package/dist/application/doctor-agent.d.ts +1 -1
- package/dist/application/doctor-agent.js +2 -2
- package/dist/application/doctor-agent.js.map +1 -1
- package/dist/application/doctor-deadline.d.ts +1 -0
- package/dist/application/doctor-deadline.js +10 -0
- package/dist/application/doctor-deadline.js.map +1 -0
- package/dist/application/doctor-image.js +9 -7
- package/dist/application/doctor-image.js.map +1 -1
- package/dist/application/doctor-sandbox.js +4 -4
- package/dist/application/doctor-sandbox.js.map +1 -1
- package/dist/application/doctor.constants.d.ts +2 -0
- package/dist/application/doctor.constants.js +2 -0
- package/dist/application/doctor.constants.js.map +1 -1
- package/dist/application/doctor.js +12 -8
- package/dist/application/doctor.js.map +1 -1
- package/dist/application/doctor.types.d.ts +2 -0
- package/dist/application/doctor.types.js.map +1 -1
- package/dist/application/file-sandbox.js +49 -11
- package/dist/application/file-sandbox.js.map +1 -1
- package/dist/application/harness-context.js +4 -2
- package/dist/application/harness-context.js.map +1 -1
- package/dist/application/harness-hook-transforms.d.ts +8 -0
- package/dist/application/harness-hook-transforms.js +115 -0
- package/dist/application/harness-hook-transforms.js.map +1 -0
- package/dist/application/harness-hooks.d.ts +5 -3
- package/dist/application/harness-hooks.js +37 -50
- package/dist/application/harness-hooks.js.map +1 -1
- package/dist/application/harness-loop.d.ts +2 -1
- package/dist/application/harness-loop.js +95 -21
- package/dist/application/harness-loop.js.map +1 -1
- package/dist/application/harness-subagent.d.ts +1 -1
- package/dist/application/harness-subagent.js +63 -12
- package/dist/application/harness-subagent.js.map +1 -1
- package/dist/application/harness.types.d.ts +3 -0
- package/dist/application/harness.types.js.map +1 -1
- package/dist/application/hook-invocation.d.ts +4 -0
- package/dist/application/hook-invocation.js +59 -0
- package/dist/application/hook-invocation.js.map +1 -0
- package/dist/application/queue-worker.js +51 -9
- package/dist/application/queue-worker.js.map +1 -1
- package/dist/application/queue-worker.types.d.ts +1 -0
- package/dist/application/queue-worker.types.js.map +1 -1
- package/dist/application/recipes/native-schemas.constants.js +1439 -218
- package/dist/application/recipes/native-schemas.constants.js.map +1 -1
- package/dist/application/recovery-inspection.js +4 -1
- package/dist/application/recovery-inspection.js.map +1 -1
- package/dist/application/remote-download.js +15 -5
- package/dist/application/remote-download.js.map +1 -1
- package/dist/application/remote-paths.d.ts +1 -0
- package/dist/application/remote-paths.js +7 -0
- package/dist/application/remote-paths.js.map +1 -0
- package/dist/application/remote-validation.js +40 -9
- package/dist/application/remote-validation.js.map +1 -1
- package/dist/application/remote-workspace.js +17 -2
- package/dist/application/remote-workspace.js.map +1 -1
- package/dist/application/repetition-watchdog.js +3 -1
- package/dist/application/repetition-watchdog.js.map +1 -1
- package/dist/application/sandbox-attach.js +38 -9
- package/dist/application/sandbox-attach.js.map +1 -1
- package/dist/application/sandbox-dispatch.js +7 -1
- package/dist/application/sandbox-dispatch.js.map +1 -1
- package/dist/application/schedules.js +2 -0
- package/dist/application/schedules.js.map +1 -1
- package/dist/application/secrets.js +4 -0
- package/dist/application/secrets.js.map +1 -1
- package/dist/application/tool-execution.js +28 -8
- package/dist/application/tool-execution.js.map +1 -1
- package/dist/application/transport-retention.js +3 -3
- package/dist/application/transport-retention.js.map +1 -1
- package/dist/application/workspace-fingerprint.d.ts +1 -0
- package/dist/application/workspace-fingerprint.js +19 -4
- package/dist/application/workspace-fingerprint.js.map +1 -1
- package/dist/cli/doctor-command.js +12 -2
- package/dist/cli/doctor-command.js.map +1 -1
- package/dist/cli/image.js +2 -0
- package/dist/cli/image.js.map +1 -1
- package/dist/cli/main.constants.d.ts +14 -2
- package/dist/cli/main.constants.js +27 -1
- package/dist/cli/main.constants.js.map +1 -1
- package/dist/cli/project-settings.js +7 -1
- package/dist/cli/project-settings.js.map +1 -1
- package/dist/cli/recipe-yaml.js +8 -0
- package/dist/cli/recipe-yaml.js.map +1 -1
- package/dist/cli/recovery-command.js +3 -0
- package/dist/cli/recovery-command.js.map +1 -1
- package/dist/cli/recovery-unlock-command.d.ts +2 -0
- package/dist/cli/recovery-unlock-command.js +19 -0
- package/dist/cli/recovery-unlock-command.js.map +1 -0
- package/dist/cli/scaffold-files.js +4 -1
- package/dist/cli/scaffold-files.js.map +1 -1
- package/dist/cli/scaffold-validation.js +3 -0
- package/dist/cli/scaffold-validation.js.map +1 -1
- package/dist/cli/scaffold.js +2 -0
- package/dist/cli/scaffold.js.map +1 -1
- package/dist/domain/agent-hook.types.d.ts +28 -0
- package/dist/domain/agent-hook.types.js +2 -0
- package/dist/domain/agent-hook.types.js.map +1 -0
- package/dist/domain/agent-profile-hooks.d.ts +2 -0
- package/dist/domain/agent-profile-hooks.js +52 -0
- package/dist/domain/agent-profile-hooks.js.map +1 -0
- package/dist/domain/agent-profile-permissions.js +11 -1
- package/dist/domain/agent-profile-permissions.js.map +1 -1
- package/dist/domain/agent-profile.constants.d.ts +1 -0
- package/dist/domain/agent-profile.constants.js +9 -0
- package/dist/domain/agent-profile.constants.js.map +1 -1
- package/dist/domain/agent-profile.js +6 -1
- package/dist/domain/agent-profile.js.map +1 -1
- package/dist/domain/agent-profile.types.d.ts +11 -0
- package/dist/domain/agent-profile.types.js.map +1 -1
- package/dist/domain/agent.types.d.ts +2 -0
- package/dist/domain/agent.types.js.map +1 -1
- package/dist/domain/container-image.d.ts +1 -0
- package/dist/domain/container-image.js +8 -0
- package/dist/domain/container-image.js.map +1 -0
- package/dist/domain/context.constants.d.ts +1 -0
- package/dist/domain/context.constants.js +1 -0
- package/dist/domain/context.constants.js.map +1 -1
- package/dist/domain/context.js +6 -1
- package/dist/domain/context.js.map +1 -1
- package/dist/domain/egress.js +6 -1
- package/dist/domain/egress.js.map +1 -1
- package/dist/domain/glob.js +1 -1
- package/dist/domain/glob.js.map +1 -1
- package/dist/domain/harness.js +4 -1
- package/dist/domain/harness.js.map +1 -1
- package/dist/domain/hook-command.d.ts +2 -0
- package/dist/domain/hook-command.js +26 -0
- package/dist/domain/hook-command.js.map +1 -0
- package/dist/domain/hook-command.types.d.ts +7 -0
- package/dist/domain/hook-command.types.js +2 -0
- package/dist/domain/hook-command.types.js.map +1 -0
- package/dist/domain/hook-decision.d.ts +2 -0
- package/dist/domain/hook-decision.js +38 -0
- package/dist/domain/hook-decision.js.map +1 -0
- package/dist/domain/hook.constants.d.ts +3 -0
- package/dist/domain/hook.constants.js +31 -1
- package/dist/domain/hook.constants.js.map +1 -1
- package/dist/domain/hook.js +49 -5
- package/dist/domain/hook.js.map +1 -1
- package/dist/domain/hook.types.d.ts +82 -5
- package/dist/domain/hook.types.js.map +1 -1
- package/dist/domain/mcp-server.constants.d.ts +6 -0
- package/dist/domain/mcp-server.constants.js +6 -0
- package/dist/domain/mcp-server.constants.js.map +1 -1
- package/dist/domain/mcp-server.js +1 -0
- package/dist/domain/mcp-server.js.map +1 -1
- package/dist/domain/permissions.types.d.ts +1 -0
- package/dist/domain/permissions.types.js.map +1 -1
- package/dist/domain/pricing.js +4 -2
- package/dist/domain/pricing.js.map +1 -1
- package/dist/domain/redaction.js +6 -3
- package/dist/domain/redaction.js.map +1 -1
- package/dist/domain/secret-fault.d.ts +4 -0
- package/dist/domain/secret-fault.js +11 -0
- package/dist/domain/secret-fault.js.map +1 -0
- package/dist/domain/secrets.js +3 -2
- package/dist/domain/secrets.js.map +1 -1
- package/dist/domain/steering.js +2 -0
- package/dist/domain/steering.js.map +1 -1
- package/dist/domain/steering.types.d.ts +1 -1
- package/dist/domain/steering.types.js.map +1 -1
- package/dist/domain/transport.js +5 -1
- package/dist/domain/transport.js.map +1 -1
- package/dist/domain/trigger-payload.d.ts +4 -0
- package/dist/domain/trigger-payload.js +15 -0
- package/dist/domain/trigger-payload.js.map +1 -0
- package/dist/domain/workflow/checkpoint.js +19 -2
- package/dist/domain/workflow/checkpoint.js.map +1 -1
- package/dist/domain/workflow/retry.constants.d.ts +1 -0
- package/dist/domain/workflow/retry.constants.js +1 -0
- package/dist/domain/workflow/retry.constants.js.map +1 -1
- package/dist/domain/workflow/retry.js +6 -2
- package/dist/domain/workflow/retry.js.map +1 -1
- package/dist/domain/workflow/task-runner.js +5 -3
- package/dist/domain/workflow/task-runner.js.map +1 -1
- package/dist/domain/workflow/workflow-state.js +2 -1
- package/dist/domain/workflow/workflow-state.js.map +1 -1
- package/dist/domain/workflow.types.d.ts +1 -0
- package/dist/domain/workflow.types.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/conversations/transcript-store.js +32 -5
- package/dist/infrastructure/conversations/transcript-store.js.map +1 -1
- package/dist/infrastructure/files.d.ts +1 -0
- package/dist/infrastructure/files.js +8 -4
- package/dist/infrastructure/files.js.map +1 -1
- package/dist/infrastructure/git/integration-candidate.js +3 -2
- package/dist/infrastructure/git/integration-candidate.js.map +1 -1
- package/dist/infrastructure/git/lock-recovery.d.ts +3 -0
- package/dist/infrastructure/git/lock-recovery.js +58 -0
- package/dist/infrastructure/git/lock-recovery.js.map +1 -0
- package/dist/infrastructure/git/lock-recovery.types.d.ts +11 -0
- package/dist/infrastructure/git/lock-recovery.types.js +2 -0
- package/dist/infrastructure/git/lock-recovery.types.js.map +1 -0
- package/dist/infrastructure/git/managed-worktree.js +5 -8
- package/dist/infrastructure/git/managed-worktree.js.map +1 -1
- package/dist/infrastructure/git/verification-command.js +35 -30
- package/dist/infrastructure/git/verification-command.js.map +1 -1
- package/dist/infrastructure/git/workspace-lease.js +12 -5
- package/dist/infrastructure/git/workspace-lease.js.map +1 -1
- package/dist/infrastructure/hook-configuration.constants.d.ts +1 -0
- package/dist/infrastructure/hook-configuration.constants.js +59 -0
- package/dist/infrastructure/hook-configuration.constants.js.map +1 -0
- package/dist/infrastructure/hook-configuration.d.ts +3 -0
- package/dist/infrastructure/hook-configuration.js +16 -0
- package/dist/infrastructure/hook-configuration.js.map +1 -0
- package/dist/infrastructure/hook-configuration.types.d.ts +14 -0
- package/dist/infrastructure/hook-configuration.types.js +2 -0
- package/dist/infrastructure/hook-configuration.types.js.map +1 -0
- package/dist/infrastructure/http-transport.constants.d.ts +1 -0
- package/dist/infrastructure/http-transport.constants.js +14 -0
- package/dist/infrastructure/http-transport.constants.js.map +1 -0
- package/dist/infrastructure/http-transport.d.ts +7 -0
- package/dist/infrastructure/http-transport.js +19 -0
- package/dist/infrastructure/http-transport.js.map +1 -0
- package/dist/infrastructure/input-failure.d.ts +4 -0
- package/dist/infrastructure/input-failure.js +14 -0
- package/dist/infrastructure/input-failure.js.map +1 -0
- package/dist/infrastructure/inspection-file.d.ts +1 -0
- package/dist/infrastructure/inspection-file.js +8 -2
- package/dist/infrastructure/inspection-file.js.map +1 -1
- package/dist/infrastructure/local-transport.js +16 -11
- package/dist/infrastructure/local-transport.js.map +1 -1
- package/dist/infrastructure/process.constants.d.ts +1 -0
- package/dist/infrastructure/process.constants.js +1 -0
- package/dist/infrastructure/process.constants.js.map +1 -1
- package/dist/infrastructure/process.d.ts +1 -0
- package/dist/infrastructure/process.js +48 -8
- package/dist/infrastructure/process.js.map +1 -1
- package/dist/infrastructure/reservation-ledger.d.ts +2 -0
- package/dist/infrastructure/reservation-ledger.js +84 -0
- package/dist/infrastructure/reservation-ledger.js.map +1 -0
- package/dist/infrastructure/reservation-recovery.d.ts +4 -0
- package/dist/infrastructure/reservation-recovery.js +28 -0
- package/dist/infrastructure/reservation-recovery.js.map +1 -0
- package/dist/infrastructure/s3-tombstones.d.ts +3 -0
- package/dist/infrastructure/s3-tombstones.js +46 -0
- package/dist/infrastructure/s3-tombstones.js.map +1 -0
- package/dist/infrastructure/s3-tombstones.types.d.ts +6 -0
- package/dist/infrastructure/s3-tombstones.types.js +2 -0
- package/dist/infrastructure/s3-tombstones.types.js.map +1 -0
- package/dist/infrastructure/s3-transport.d.ts +2 -0
- package/dist/infrastructure/s3-transport.js +4 -2
- package/dist/infrastructure/s3-transport.js.map +1 -1
- package/dist/infrastructure/sse.js +6 -3
- package/dist/infrastructure/sse.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq-connection.d.ts +3 -0
- package/dist/infrastructure/task-queue-bullmq-connection.js +32 -0
- package/dist/infrastructure/task-queue-bullmq-connection.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.d.ts +8 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.js +2 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq-resources.js +21 -15
- package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq-state.d.ts +1 -0
- package/dist/infrastructure/task-queue-bullmq-state.js +1 -0
- package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq.constants.d.ts +2 -1
- package/dist/infrastructure/task-queue-bullmq.constants.js +2 -1
- package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq.js +10 -1
- package/dist/infrastructure/task-queue-bullmq.js.map +1 -1
- package/dist/infrastructure/task-queue.js +13 -4
- package/dist/infrastructure/task-queue.js.map +1 -1
- package/dist/infrastructure/transfer-copy.d.ts +1 -0
- package/dist/infrastructure/transfer-copy.js +16 -1
- package/dist/infrastructure/transfer-copy.js.map +1 -1
- package/dist/infrastructure/transport-archive.d.ts +3 -3
- package/dist/infrastructure/transport-archive.js +71 -48
- package/dist/infrastructure/transport-archive.js.map +1 -1
- package/dist/infrastructure/transport-archive.types.d.ts +3 -0
- package/dist/infrastructure/transport-archive.types.js.map +1 -1
- package/dist/infrastructure/transport-checkpoint.js +14 -4
- package/dist/infrastructure/transport-checkpoint.js.map +1 -1
- package/dist/infrastructure/transport-envelope.d.ts +1 -0
- package/dist/infrastructure/transport-envelope.js +22 -5
- package/dist/infrastructure/transport-envelope.js.map +1 -1
- package/dist/infrastructure/transport-reservations.constants.d.ts +2 -0
- package/dist/infrastructure/transport-reservations.constants.js +3 -0
- package/dist/infrastructure/transport-reservations.constants.js.map +1 -0
- package/dist/infrastructure/transport-reservations.d.ts +1 -2
- package/dist/infrastructure/transport-reservations.js +18 -22
- package/dist/infrastructure/transport-reservations.js.map +1 -1
- package/dist/infrastructure/transport-reservations.types.d.ts +23 -2
- package/dist/infrastructure/transport-reservations.types.js.map +1 -1
- package/dist/infrastructure/trigger-server.js +12 -9
- package/dist/infrastructure/trigger-server.js.map +1 -1
- package/dist/infrastructure/trigger-server.types.d.ts +1 -1
- package/dist/infrastructure/trigger-server.types.js.map +1 -1
- package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js +1 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -1
- package/dist/infrastructure/workspace-lock.constants.d.ts +2 -1
- package/dist/infrastructure/workspace-lock.constants.js +2 -1
- package/dist/infrastructure/workspace-lock.constants.js.map +1 -1
- package/dist/infrastructure/workspace-lock.js +48 -10
- package/dist/infrastructure/workspace-lock.js.map +1 -1
- package/dist/providers/cloud-cache.js +14 -9
- package/dist/providers/cloud-cache.js.map +1 -1
- package/dist/providers/cloud-cache.types.d.ts +1 -0
- package/dist/providers/cloud-cache.types.js.map +1 -1
- package/dist/providers/cloud-files.js +3 -4
- package/dist/providers/cloud-files.js.map +1 -1
- package/dist/providers/cloud-input.constants.d.ts +2 -2
- package/dist/providers/cloud-input.constants.js +2 -1
- package/dist/providers/cloud-input.constants.js.map +1 -1
- package/dist/providers/cloud-input.d.ts +2 -2
- package/dist/providers/cloud-input.js +14 -3
- package/dist/providers/cloud-input.js.map +1 -1
- package/dist/providers/container-command.js +30 -5
- package/dist/providers/container-command.js.map +1 -1
- package/dist/providers/container-egress.constants.d.ts +7 -0
- package/dist/providers/container-egress.constants.js +8 -0
- package/dist/providers/container-egress.constants.js.map +1 -0
- package/dist/providers/container-egress.d.ts +7 -0
- package/dist/providers/container-egress.js +101 -0
- package/dist/providers/container-egress.js.map +1 -0
- package/dist/providers/container-files.js +10 -11
- package/dist/providers/container-files.js.map +1 -1
- package/dist/providers/container-mounts.js +3 -0
- package/dist/providers/container-mounts.js.map +1 -1
- package/dist/providers/container-network.js +16 -1
- package/dist/providers/container-network.js.map +1 -1
- package/dist/providers/container-plan.js +7 -10
- package/dist/providers/container-plan.js.map +1 -1
- package/dist/providers/container-recovery.js +8 -3
- package/dist/providers/container-recovery.js.map +1 -1
- package/dist/providers/container.js +36 -5
- package/dist/providers/container.js.map +1 -1
- package/dist/providers/container.types.d.ts +1 -0
- package/dist/providers/container.types.js.map +1 -1
- package/dist/providers/daytona-command.constants.js +4 -1
- package/dist/providers/daytona-command.constants.js.map +1 -1
- package/dist/providers/daytona-command.js +39 -16
- package/dist/providers/daytona-command.js.map +1 -1
- package/dist/providers/daytona-files.js +5 -5
- package/dist/providers/daytona-files.js.map +1 -1
- package/dist/providers/daytona-output.js +11 -3
- package/dist/providers/daytona-output.js.map +1 -1
- package/dist/providers/daytona.js +11 -9
- package/dist/providers/daytona.js.map +1 -1
- package/dist/providers/egress/address.d.ts +3 -0
- package/dist/providers/egress/address.js +26 -0
- package/dist/providers/egress/address.js.map +1 -0
- package/dist/providers/egress/egress.constants.d.ts +22 -0
- package/dist/providers/egress/egress.constants.js +23 -0
- package/dist/providers/egress/egress.constants.js.map +1 -0
- package/dist/providers/egress/egress.types.d.ts +11 -0
- package/dist/providers/egress/egress.types.js +2 -0
- package/dist/providers/egress/egress.types.js.map +1 -0
- package/dist/providers/egress/firewall.d.ts +3 -0
- package/dist/providers/egress/firewall.js +29 -0
- package/dist/providers/egress/firewall.js.map +1 -0
- package/dist/providers/egress/main.d.ts +1 -0
- package/dist/providers/egress/main.js +90 -0
- package/dist/providers/egress/main.js.map +1 -0
- package/dist/providers/egress/policy.d.ts +5 -0
- package/dist/providers/egress/policy.js +54 -0
- package/dist/providers/egress/policy.js.map +1 -0
- package/dist/providers/egress/proxy.d.ts +2 -0
- package/dist/providers/egress/proxy.js +129 -0
- package/dist/providers/egress/proxy.js.map +1 -0
- package/dist/providers/egress/tls-hello.d.ts +1 -0
- package/dist/providers/egress/tls-hello.js +75 -0
- package/dist/providers/egress/tls-hello.js.map +1 -0
- package/dist/providers/firecracker-command.js +12 -6
- package/dist/providers/firecracker-command.js.map +1 -1
- package/dist/providers/local-files.d.ts +2 -0
- package/dist/providers/local-files.js +24 -0
- package/dist/providers/local-files.js.map +1 -0
- package/dist/providers/local.js +2 -20
- package/dist/providers/local.js.map +1 -1
- package/dist/providers/vercel-command.js +33 -9
- package/dist/providers/vercel-command.js.map +1 -1
- package/dist/providers/vercel.js +11 -9
- package/dist/providers/vercel.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/recipe-configuration.schema.json +2164 -35
- package/recipe.schema.json +2512 -404
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
import { egressRuntime } from "./egress.constants.js";
|
|
3
|
+
import { egressCidr } from "./address.js";
|
|
4
|
+
export function egressFirewall(policy) {
|
|
5
|
+
const destination = (value) => {
|
|
6
|
+
const cidr = egressCidr(value);
|
|
7
|
+
return `${isIP(cidr.split("/")[0]) === 4 ? "ip" : "ip6"} daddr ${cidr}`;
|
|
8
|
+
};
|
|
9
|
+
return `table inet outpost_egress {
|
|
10
|
+
set live { type nf_proto; flags timeout; timeout 3s; }
|
|
11
|
+
chain permitted {
|
|
12
|
+
${(policy.denyCidrs ?? []).map((cidr) => `${destination(cidr)} drop`).join("\n ")}
|
|
13
|
+
meta skuid ${egressRuntime.uid} meta l4proto { tcp, udp } accept
|
|
14
|
+
${(policy.allowCidrs ?? []).map((cidr) => `${destination(cidr)} meta l4proto { tcp, udp } accept`).join("\n ")}
|
|
15
|
+
}
|
|
16
|
+
chain output {
|
|
17
|
+
type filter hook output priority -150; policy drop;
|
|
18
|
+
oifname "lo" ip daddr 127.0.0.1 accept
|
|
19
|
+
oifname "lo" ip6 daddr ::1 accept
|
|
20
|
+
ip6 hoplimit 255 icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit } accept
|
|
21
|
+
meta nfproto @live jump permitted
|
|
22
|
+
}
|
|
23
|
+
chain forward { type filter hook forward priority 0; policy drop; }
|
|
24
|
+
}`;
|
|
25
|
+
}
|
|
26
|
+
export function egressHeartbeat() {
|
|
27
|
+
return "flush set inet outpost_egress live\nadd element inet outpost_egress live { ipv4 timeout 3s, ipv6 timeout 3s }";
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=firewall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firewall.js","sourceRoot":"","sources":["../../../src/providers/egress/firewall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,UAAU,cAAc,CAAC,MAAiB;IAC9C,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC;IAC3E,CAAC,CAAC;IACF,OAAO;;;IAGL,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;eACrE,aAAa,CAAC,GAAG;IAC5B,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;EAU/G,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,+GAA+G,CAAC;AACzH,CAAC","sourcesContent":["import { isIP } from \"node:net\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport type { Allowlist } from \"./egress.types.ts\";\nimport { egressCidr } from \"./address.ts\";\n\nexport function egressFirewall(policy: Allowlist): string {\n const destination = (value: string) => {\n const cidr = egressCidr(value);\n return `${isIP(cidr.split(\"/\")[0]!) === 4 ? \"ip\" : \"ip6\"} daddr ${cidr}`;\n };\n return `table inet outpost_egress {\n set live { type nf_proto; flags timeout; timeout 3s; }\n chain permitted {\n ${(policy.denyCidrs ?? []).map((cidr) => `${destination(cidr)} drop`).join(\"\\n \")}\n meta skuid ${egressRuntime.uid} meta l4proto { tcp, udp } accept\n ${(policy.allowCidrs ?? []).map((cidr) => `${destination(cidr)} meta l4proto { tcp, udp } accept`).join(\"\\n \")}\n }\n chain output {\n type filter hook output priority -150; policy drop;\n oifname \"lo\" ip daddr 127.0.0.1 accept\n oifname \"lo\" ip6 daddr ::1 accept\n ip6 hoplimit 255 icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit } accept\n meta nfproto @live jump permitted\n }\n chain forward { type filter hook forward priority 0; policy drop; }\n}`;\n}\n\nexport function egressHeartbeat(): string {\n return \"flush set inet outpost_egress live\\nadd element inet outpost_egress live { ipv4 timeout 3s, ipv6 timeout 3s }\";\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
2
|
+
import { writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { validateEgress } from "../../domain/egress.js";
|
|
5
|
+
import { egressRuntime } from "./egress.constants.js";
|
|
6
|
+
import { egressFirewall, egressHeartbeat } from "./firewall.js";
|
|
7
|
+
import { createEgressProxy } from "./proxy.js";
|
|
8
|
+
function run() {
|
|
9
|
+
if (process.argv[2] === "health") {
|
|
10
|
+
if (process.argv[3] !== String(egressRuntime.version) ||
|
|
11
|
+
readFileSync(egressRuntime.ready, "utf8") !==
|
|
12
|
+
String(egressRuntime.version))
|
|
13
|
+
throw new Error("Egress not ready");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const policy = validateEgress(JSON.parse(process.env.OUTPOST_EGRESS_POLICY ?? "null"));
|
|
17
|
+
if (policy?.mode !== "allowlist")
|
|
18
|
+
throw new Error("Allowlist required");
|
|
19
|
+
if (process.argv[2] === "proxy") {
|
|
20
|
+
process.setgroups([]);
|
|
21
|
+
process.setgid(egressRuntime.uid);
|
|
22
|
+
process.setuid(egressRuntime.uid);
|
|
23
|
+
const server = createEgressProxy(policy);
|
|
24
|
+
server.listen(egressRuntime.port, "127.0.0.1", () => process.send?.("ready"));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const nft = (input) => execFileSync("nft", ["-f", "-"], {
|
|
28
|
+
input,
|
|
29
|
+
timeout: 2_000,
|
|
30
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
31
|
+
});
|
|
32
|
+
nft(egressFirewall(policy));
|
|
33
|
+
const child = spawn(process.execPath, [fileURLToPath(import.meta.url), "proxy"], { stdio: ["ignore", "inherit", "inherit", "ipc"] });
|
|
34
|
+
let heartbeat;
|
|
35
|
+
const stop = () => {
|
|
36
|
+
clearInterval(heartbeat);
|
|
37
|
+
rmSync(egressRuntime.ready, { force: true });
|
|
38
|
+
try {
|
|
39
|
+
nft("flush set inet outpost_egress live");
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
child.kill("SIGKILL");
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
child.once("message", (message) => {
|
|
46
|
+
if (message !== "ready") {
|
|
47
|
+
stop();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const refresh = () => {
|
|
51
|
+
try {
|
|
52
|
+
nft(egressHeartbeat());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
stop();
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
nft(egressHeartbeat());
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
stop();
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
writeFileSync(egressRuntime.ready, String(egressRuntime.version), {
|
|
68
|
+
mode: 0o600,
|
|
69
|
+
});
|
|
70
|
+
heartbeat = setInterval(refresh, egressRuntime.heartbeatMs);
|
|
71
|
+
});
|
|
72
|
+
child.once("exit", () => {
|
|
73
|
+
stop();
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
});
|
|
76
|
+
child.once("error", () => {
|
|
77
|
+
stop();
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
});
|
|
80
|
+
process.once("SIGTERM", stop);
|
|
81
|
+
process.once("SIGINT", stop);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
run();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
process.stderr.write("Outpost egress startup or health check failed\n");
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/providers/egress/main.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, execFileSync } from \"node:child_process\";\nimport { writeFileSync, readFileSync, rmSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { validateEgress } from \"../../domain/egress.ts\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport { egressFirewall, egressHeartbeat } from \"./firewall.ts\";\nimport { createEgressProxy } from \"./proxy.ts\";\n\nfunction run(): void {\n if (process.argv[2] === \"health\") {\n if (\n process.argv[3] !== String(egressRuntime.version) ||\n readFileSync(egressRuntime.ready, \"utf8\") !==\n String(egressRuntime.version)\n )\n throw new Error(\"Egress not ready\");\n return;\n }\n const policy = validateEgress(\n JSON.parse(process.env.OUTPOST_EGRESS_POLICY ?? \"null\"),\n );\n if (policy?.mode !== \"allowlist\") throw new Error(\"Allowlist required\");\n if (process.argv[2] === \"proxy\") {\n process.setgroups!([]);\n process.setgid!(egressRuntime.uid);\n process.setuid!(egressRuntime.uid);\n const server = createEgressProxy(policy);\n server.listen(egressRuntime.port, \"127.0.0.1\", () =>\n process.send?.(\"ready\"),\n );\n return;\n }\n const nft = (input: string) =>\n execFileSync(\"nft\", [\"-f\", \"-\"], {\n input,\n timeout: 2_000,\n stdio: [\"pipe\", \"ignore\", \"pipe\"],\n });\n nft(egressFirewall(policy));\n const child = spawn(\n process.execPath,\n [fileURLToPath(import.meta.url), \"proxy\"],\n { stdio: [\"ignore\", \"inherit\", \"inherit\", \"ipc\"] },\n );\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n const stop = () => {\n clearInterval(heartbeat);\n rmSync(egressRuntime.ready, { force: true });\n try {\n nft(\"flush set inet outpost_egress live\");\n } finally {\n child.kill(\"SIGKILL\");\n }\n };\n child.once(\"message\", (message: unknown) => {\n if (message !== \"ready\") {\n stop();\n return;\n }\n const refresh = () => {\n try {\n nft(egressHeartbeat());\n } catch {\n stop();\n process.exitCode = 1;\n }\n };\n try {\n nft(egressHeartbeat());\n } catch {\n stop();\n process.exitCode = 1;\n return;\n }\n writeFileSync(egressRuntime.ready, String(egressRuntime.version), {\n mode: 0o600,\n });\n heartbeat = setInterval(refresh, egressRuntime.heartbeatMs);\n });\n child.once(\"exit\", () => {\n stop();\n process.exitCode = 1;\n });\n child.once(\"error\", () => {\n stop();\n process.exitCode = 1;\n });\n process.once(\"SIGTERM\", stop);\n process.once(\"SIGINT\", stop);\n}\n\ntry {\n run();\n} catch {\n process.stderr.write(\"Outpost egress startup or health check failed\\n\");\n process.exitCode = 1;\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { BlockList, isIP } from "node:net";
|
|
3
|
+
import { egressAddress, egressCidr } from "./address.js";
|
|
4
|
+
function subnets(cidrs) {
|
|
5
|
+
const result = { ipv4: new BlockList(), ipv6: new BlockList() };
|
|
6
|
+
for (const cidr of cidrs.map(egressCidr)) {
|
|
7
|
+
const [address, prefix] = cidr.split("/");
|
|
8
|
+
const family = isIP(address) === 4 ? "ipv4" : "ipv6";
|
|
9
|
+
result[family].addSubnet(address, Number(prefix), isIP(address) === 4 ? "ipv4" : "ipv6");
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
export function egressAccess(policy) {
|
|
14
|
+
const denied = subnets(policy.denyCidrs ?? []);
|
|
15
|
+
const allowed = subnets(policy.allowCidrs ?? []);
|
|
16
|
+
const local = subnets(["127.0.0.0/8", "::1/128", "0.0.0.0/8", "::/128"]);
|
|
17
|
+
const domains = (policy.domains ?? []).map((name) => name.toLowerCase());
|
|
18
|
+
const matches = (list, address) => {
|
|
19
|
+
const family = address.family === 4 ? "ipv4" : "ipv6";
|
|
20
|
+
return list[family].check(address.address, family);
|
|
21
|
+
};
|
|
22
|
+
const domain = (hostname) => {
|
|
23
|
+
if (hostname.length > 253 ||
|
|
24
|
+
!hostname
|
|
25
|
+
.split(".")
|
|
26
|
+
.every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(label)))
|
|
27
|
+
return false;
|
|
28
|
+
return domains.some((pattern) => pattern.startsWith("*.")
|
|
29
|
+
? hostname.toLowerCase().endsWith(pattern.slice(1))
|
|
30
|
+
: hostname.toLowerCase() === pattern);
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
domain,
|
|
34
|
+
async resolve(hostname, resolver = (name) => lookup(name, { all: true })) {
|
|
35
|
+
const version = isIP(hostname);
|
|
36
|
+
if (!version && !domain(hostname))
|
|
37
|
+
throw new Error("Egress destination denied");
|
|
38
|
+
const addresses = version
|
|
39
|
+
? [{ address: hostname, family: version }]
|
|
40
|
+
: await resolver(hostname);
|
|
41
|
+
const selected = addresses
|
|
42
|
+
.filter((item) => isIP(item.address) === item.family)
|
|
43
|
+
.map((item) => egressAddress(item.address))
|
|
44
|
+
.find((address) => isIP(address.address) === address.family &&
|
|
45
|
+
!matches(local, address) &&
|
|
46
|
+
!matches(denied, address) &&
|
|
47
|
+
(domain(hostname) || matches(allowed, address)));
|
|
48
|
+
if (!selected)
|
|
49
|
+
throw new Error("Egress destination denied");
|
|
50
|
+
return selected;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/providers/egress/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { BlockList, isIP } from \"node:net\";\nimport { egressAddress, egressCidr } from \"./address.ts\";\nimport type {\n Allowlist,\n EgressAddress,\n EgressResolver,\n} from \"./egress.types.ts\";\n\nfunction subnets(cidrs: readonly string[]) {\n const result = { ipv4: new BlockList(), ipv6: new BlockList() };\n for (const cidr of cidrs.map(egressCidr)) {\n const [address, prefix] = cidr.split(\"/\");\n const family = isIP(address!) === 4 ? \"ipv4\" : \"ipv6\";\n result[family].addSubnet(\n address!,\n Number(prefix),\n isIP(address!) === 4 ? \"ipv4\" : \"ipv6\",\n );\n }\n return result;\n}\n\nexport function egressAccess(policy: Allowlist) {\n const denied = subnets(policy.denyCidrs ?? []);\n const allowed = subnets(policy.allowCidrs ?? []);\n const local = subnets([\"127.0.0.0/8\", \"::1/128\", \"0.0.0.0/8\", \"::/128\"]);\n const domains = (policy.domains ?? []).map((name) => name.toLowerCase());\n const matches = (\n list: ReturnType<typeof subnets>,\n address: EgressAddress,\n ) => {\n const family = address.family === 4 ? \"ipv4\" : \"ipv6\";\n return list[family].check(address.address, family);\n };\n const domain = (hostname: string) => {\n if (\n hostname.length > 253 ||\n !hostname\n .split(\".\")\n .every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(label))\n )\n return false;\n return domains.some((pattern) =>\n pattern.startsWith(\"*.\")\n ? hostname.toLowerCase().endsWith(pattern.slice(1))\n : hostname.toLowerCase() === pattern,\n );\n };\n return {\n domain,\n async resolve(\n hostname: string,\n resolver: EgressResolver = (name) => lookup(name, { all: true }),\n ): Promise<EgressAddress> {\n const version = isIP(hostname);\n if (!version && !domain(hostname))\n throw new Error(\"Egress destination denied\");\n const addresses = version\n ? [{ address: hostname, family: version }]\n : await resolver(hostname);\n const selected = addresses\n .filter((item) => isIP(item.address) === item.family)\n .map((item) => egressAddress(item.address))\n .find(\n (address) =>\n isIP(address.address) === address.family &&\n !matches(local, address) &&\n !matches(denied, address) &&\n (domain(hostname) || matches(allowed, address)),\n );\n if (!selected) throw new Error(\"Egress destination denied\");\n return selected;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createServer, request } from "node:http";
|
|
2
|
+
import { connect, isIP } from "node:net";
|
|
3
|
+
import { egressRuntime } from "./egress.constants.js";
|
|
4
|
+
import { egressAccess } from "./policy.js";
|
|
5
|
+
import { tlsServerName } from "./tls-hello.js";
|
|
6
|
+
function reject(socket) {
|
|
7
|
+
socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
8
|
+
}
|
|
9
|
+
export function createEgressProxy(policy, resolver) {
|
|
10
|
+
const access = egressAccess(policy);
|
|
11
|
+
const server = createServer({ maxHeaderSize: egressRuntime.maxHeaderBytes }, (incoming, outgoing) => {
|
|
12
|
+
void (async () => {
|
|
13
|
+
const target = new URL(incoming.url ?? "");
|
|
14
|
+
if (target.protocol !== "http:" ||
|
|
15
|
+
(target.port && target.port !== "80") ||
|
|
16
|
+
target.username ||
|
|
17
|
+
target.password)
|
|
18
|
+
throw new Error("HTTP destination required");
|
|
19
|
+
const hostname = target.hostname.replace(/^\[|\]$/g, "");
|
|
20
|
+
const address = await access.resolve(hostname, resolver);
|
|
21
|
+
if (incoming.destroyed || outgoing.destroyed)
|
|
22
|
+
return;
|
|
23
|
+
const headers = {
|
|
24
|
+
...incoming.headers,
|
|
25
|
+
host: target.host,
|
|
26
|
+
connection: "close",
|
|
27
|
+
};
|
|
28
|
+
for (const key of [
|
|
29
|
+
"proxy-authorization",
|
|
30
|
+
"proxy-connection",
|
|
31
|
+
"upgrade",
|
|
32
|
+
...(incoming.headers.connection ?? "")
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.split(",")
|
|
35
|
+
.map((value) => value.trim()),
|
|
36
|
+
])
|
|
37
|
+
delete headers[key];
|
|
38
|
+
headers.host = target.host;
|
|
39
|
+
headers.connection = "close";
|
|
40
|
+
const upstream = request({
|
|
41
|
+
hostname: address.address,
|
|
42
|
+
family: address.family,
|
|
43
|
+
port: 80,
|
|
44
|
+
method: incoming.method,
|
|
45
|
+
path: target.pathname + target.search,
|
|
46
|
+
headers,
|
|
47
|
+
agent: false,
|
|
48
|
+
}, (response) => {
|
|
49
|
+
outgoing.writeHead(response.statusCode ?? 502, response.headers);
|
|
50
|
+
response.pipe(outgoing);
|
|
51
|
+
response.on("error", () => outgoing.destroy());
|
|
52
|
+
});
|
|
53
|
+
upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());
|
|
54
|
+
upstream.on("error", () => {
|
|
55
|
+
if (outgoing.headersSent) {
|
|
56
|
+
outgoing.destroy();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
outgoing.writeHead(502).end();
|
|
60
|
+
});
|
|
61
|
+
incoming.on("aborted", () => upstream.destroy());
|
|
62
|
+
outgoing.on("close", () => upstream.destroy());
|
|
63
|
+
incoming.pipe(upstream);
|
|
64
|
+
})().catch(() => outgoing.writeHead(403).end());
|
|
65
|
+
});
|
|
66
|
+
server.on("connect", (request, socket, head) => {
|
|
67
|
+
socket.on("error", () => socket.destroy());
|
|
68
|
+
void (async () => {
|
|
69
|
+
const authority = request.url ?? "";
|
|
70
|
+
const target = new URL(`https://${authority}`);
|
|
71
|
+
if (target.username ||
|
|
72
|
+
target.password ||
|
|
73
|
+
target.pathname !== "/" ||
|
|
74
|
+
target.search ||
|
|
75
|
+
target.hash ||
|
|
76
|
+
!authority.endsWith(":443"))
|
|
77
|
+
throw new Error("HTTPS destination required");
|
|
78
|
+
const hostname = target.hostname.replace(/^\[|\]$/g, "");
|
|
79
|
+
const address = await access.resolve(hostname, resolver);
|
|
80
|
+
if (socket.destroyed)
|
|
81
|
+
return;
|
|
82
|
+
socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
83
|
+
let buffered = head;
|
|
84
|
+
const timeout = setTimeout(() => socket.destroy(), egressRuntime.connectMs);
|
|
85
|
+
socket.once("close", () => clearTimeout(timeout));
|
|
86
|
+
const receive = (chunk) => {
|
|
87
|
+
try {
|
|
88
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
89
|
+
if (!isIP(hostname)) {
|
|
90
|
+
const name = tlsServerName(buffered);
|
|
91
|
+
if (name === undefined)
|
|
92
|
+
return;
|
|
93
|
+
if (name !== hostname.toLowerCase())
|
|
94
|
+
throw new Error("TLS server name mismatch");
|
|
95
|
+
}
|
|
96
|
+
socket.pause();
|
|
97
|
+
socket.removeListener("data", receive);
|
|
98
|
+
const upstream = connect({
|
|
99
|
+
host: address.address,
|
|
100
|
+
family: address.family,
|
|
101
|
+
port: 443,
|
|
102
|
+
});
|
|
103
|
+
upstream.on("error", () => socket.destroy());
|
|
104
|
+
socket.once("close", () => upstream.destroy());
|
|
105
|
+
upstream.once("close", () => socket.end());
|
|
106
|
+
upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());
|
|
107
|
+
upstream.once("connect", () => {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
upstream.write(buffered);
|
|
110
|
+
socket.pipe(upstream).pipe(socket);
|
|
111
|
+
socket.resume();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
socket.destroy();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
socket.on("data", receive);
|
|
119
|
+
receive(Buffer.alloc(0));
|
|
120
|
+
})().catch(() => reject(socket));
|
|
121
|
+
});
|
|
122
|
+
server.on("upgrade", (_request, socket) => reject(socket));
|
|
123
|
+
server.on("clientError", (_error, socket) => reject(socket));
|
|
124
|
+
server.requestTimeout = egressRuntime.idleMs;
|
|
125
|
+
server.headersTimeout = egressRuntime.connectMs;
|
|
126
|
+
server.maxConnections = egressRuntime.maxConnections;
|
|
127
|
+
return server;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../../src/providers/egress/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, request } from \"node:http\";\nimport { connect, isIP } from \"node:net\";\nimport type { Duplex } from \"node:stream\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport type { Allowlist, EgressResolver } from \"./egress.types.ts\";\nimport { egressAccess } from \"./policy.ts\";\nimport { tlsServerName } from \"./tls-hello.ts\";\n\nfunction reject(socket: Duplex): void {\n socket.end(\n \"HTTP/1.1 403 Forbidden\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n );\n}\n\nexport function createEgressProxy(\n policy: Allowlist,\n resolver?: EgressResolver,\n) {\n const access = egressAccess(policy);\n const server = createServer(\n { maxHeaderSize: egressRuntime.maxHeaderBytes },\n (incoming, outgoing) => {\n void (async () => {\n const target = new URL(incoming.url ?? \"\");\n if (\n target.protocol !== \"http:\" ||\n (target.port && target.port !== \"80\") ||\n target.username ||\n target.password\n )\n throw new Error(\"HTTP destination required\");\n const hostname = target.hostname.replace(/^\\[|\\]$/g, \"\");\n const address = await access.resolve(hostname, resolver);\n if (incoming.destroyed || outgoing.destroyed) return;\n const headers = {\n ...incoming.headers,\n host: target.host,\n connection: \"close\",\n };\n for (const key of [\n \"proxy-authorization\",\n \"proxy-connection\",\n \"upgrade\",\n ...(incoming.headers.connection ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((value) => value.trim()),\n ])\n delete headers[key as keyof typeof headers];\n headers.host = target.host;\n headers.connection = \"close\";\n const upstream = request(\n {\n hostname: address.address,\n family: address.family,\n port: 80,\n method: incoming.method,\n path: target.pathname + target.search,\n headers,\n agent: false,\n },\n (response) => {\n outgoing.writeHead(response.statusCode ?? 502, response.headers);\n response.pipe(outgoing);\n response.on(\"error\", () => outgoing.destroy());\n },\n );\n upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());\n upstream.on(\"error\", () => {\n if (outgoing.headersSent) {\n outgoing.destroy();\n return;\n }\n outgoing.writeHead(502).end();\n });\n incoming.on(\"aborted\", () => upstream.destroy());\n outgoing.on(\"close\", () => upstream.destroy());\n incoming.pipe(upstream);\n })().catch(() => outgoing.writeHead(403).end());\n },\n );\n server.on(\"connect\", (request, socket, head) => {\n socket.on(\"error\", () => socket.destroy());\n void (async () => {\n const authority = request.url ?? \"\";\n const target = new URL(`https://${authority}`);\n if (\n target.username ||\n target.password ||\n target.pathname !== \"/\" ||\n target.search ||\n target.hash ||\n !authority.endsWith(\":443\")\n )\n throw new Error(\"HTTPS destination required\");\n const hostname = target.hostname.replace(/^\\[|\\]$/g, \"\");\n const address = await access.resolve(hostname, resolver);\n if (socket.destroyed) return;\n socket.write(\"HTTP/1.1 200 Connection Established\\r\\n\\r\\n\");\n let buffered = head;\n const timeout = setTimeout(\n () => socket.destroy(),\n egressRuntime.connectMs,\n );\n socket.once(\"close\", () => clearTimeout(timeout));\n const receive = (chunk: Buffer) => {\n try {\n buffered = Buffer.concat([buffered, chunk]);\n if (!isIP(hostname)) {\n const name = tlsServerName(buffered);\n if (name === undefined) return;\n if (name !== hostname.toLowerCase())\n throw new Error(\"TLS server name mismatch\");\n }\n socket.pause();\n socket.removeListener(\"data\", receive);\n const upstream = connect({\n host: address.address,\n family: address.family,\n port: 443,\n });\n upstream.on(\"error\", () => socket.destroy());\n socket.once(\"close\", () => upstream.destroy());\n upstream.once(\"close\", () => socket.end());\n upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());\n upstream.once(\"connect\", () => {\n clearTimeout(timeout);\n upstream.write(buffered);\n socket.pipe(upstream).pipe(socket);\n socket.resume();\n });\n } catch {\n socket.destroy();\n }\n };\n socket.on(\"data\", receive);\n receive(Buffer.alloc(0));\n })().catch(() => reject(socket));\n });\n server.on(\"upgrade\", (_request, socket) => reject(socket));\n server.on(\"clientError\", (_error, socket) => reject(socket));\n server.requestTimeout = egressRuntime.idleMs;\n server.headersTimeout = egressRuntime.connectMs;\n server.maxConnections = egressRuntime.maxConnections;\n return server;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function tlsServerName(input: Buffer): string | undefined;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { egressRuntime } from "./egress.constants.js";
|
|
2
|
+
export function tlsServerName(input) {
|
|
3
|
+
if (input.length > egressRuntime.maxHelloBytes)
|
|
4
|
+
throw new Error("TLS hello too large");
|
|
5
|
+
const fragments = [];
|
|
6
|
+
let offset = 0;
|
|
7
|
+
while (offset < input.length) {
|
|
8
|
+
if (input.length - offset < 5)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (input[offset] !== 22 || input[offset + 1] !== 3)
|
|
11
|
+
throw new Error("TLS ClientHello required");
|
|
12
|
+
const end = offset + 5 + input.readUInt16BE(offset + 3);
|
|
13
|
+
if (end > input.length)
|
|
14
|
+
return undefined;
|
|
15
|
+
fragments.push(input.subarray(offset + 5, end));
|
|
16
|
+
if (fragments.length > egressRuntime.maxHelloRecords)
|
|
17
|
+
throw new Error("Too many TLS hello records");
|
|
18
|
+
offset = end;
|
|
19
|
+
const hello = Buffer.concat(fragments);
|
|
20
|
+
if (hello.length < 4)
|
|
21
|
+
continue;
|
|
22
|
+
if (hello[0] !== 1)
|
|
23
|
+
throw new Error("TLS ClientHello required");
|
|
24
|
+
const length = hello.readUIntBE(1, 3) + 4;
|
|
25
|
+
if (length > egressRuntime.maxHelloBytes)
|
|
26
|
+
throw new Error("TLS hello too large");
|
|
27
|
+
if (hello.length < length)
|
|
28
|
+
continue;
|
|
29
|
+
const data = hello.subarray(0, length);
|
|
30
|
+
let cursor = 38;
|
|
31
|
+
const take = (count) => {
|
|
32
|
+
if (count < 0 || cursor + count > data.length)
|
|
33
|
+
throw new Error("Malformed TLS hello");
|
|
34
|
+
const part = data.subarray(cursor, cursor + count);
|
|
35
|
+
cursor += count;
|
|
36
|
+
return part;
|
|
37
|
+
};
|
|
38
|
+
take(take(1)[0]);
|
|
39
|
+
take(take(2).readUInt16BE());
|
|
40
|
+
take(take(1)[0]);
|
|
41
|
+
const extensions = take(take(2).readUInt16BE());
|
|
42
|
+
if (cursor !== data.length)
|
|
43
|
+
throw new Error("Malformed TLS extensions");
|
|
44
|
+
let position = 0;
|
|
45
|
+
let name;
|
|
46
|
+
while (position < extensions.length) {
|
|
47
|
+
if (position + 4 > extensions.length)
|
|
48
|
+
throw new Error("Malformed TLS extension");
|
|
49
|
+
const kind = extensions.readUInt16BE(position);
|
|
50
|
+
const size = extensions.readUInt16BE(position + 2);
|
|
51
|
+
position += 4;
|
|
52
|
+
if (position + size > extensions.length)
|
|
53
|
+
throw new Error("Malformed TLS extension");
|
|
54
|
+
const extension = extensions.subarray(position, position + size);
|
|
55
|
+
position += size;
|
|
56
|
+
if (kind !== 0)
|
|
57
|
+
continue;
|
|
58
|
+
if (name !== undefined)
|
|
59
|
+
throw new Error("Duplicate TLS server name");
|
|
60
|
+
if (size < 5 ||
|
|
61
|
+
extension.readUInt16BE(0) !== size - 2 ||
|
|
62
|
+
extension[2] !== 0 ||
|
|
63
|
+
extension.readUInt16BE(3) !== size - 5)
|
|
64
|
+
throw new Error("Malformed TLS server name");
|
|
65
|
+
name = extension.subarray(5).toString("utf8");
|
|
66
|
+
if (!/^[a-z0-9.-]+$/i.test(name))
|
|
67
|
+
throw new Error("Invalid TLS server name");
|
|
68
|
+
}
|
|
69
|
+
if (name !== undefined)
|
|
70
|
+
return name.toLowerCase();
|
|
71
|
+
throw new Error("TLS server name required");
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=tls-hello.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-hello.js","sourceRoot":"","sources":["../../../src/providers/egress/tls-hello.ts"],"names":[],"mappings":"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","sourcesContent":["import { egressRuntime } from \"./egress.constants.ts\";\n\nexport function tlsServerName(input: Buffer): string | undefined {\n if (input.length > egressRuntime.maxHelloBytes)\n throw new Error(\"TLS hello too large\");\n const fragments: Buffer[] = [];\n let offset = 0;\n while (offset < input.length) {\n if (input.length - offset < 5) return undefined;\n if (input[offset] !== 22 || input[offset + 1] !== 3)\n throw new Error(\"TLS ClientHello required\");\n const end = offset + 5 + input.readUInt16BE(offset + 3);\n if (end > input.length) return undefined;\n fragments.push(input.subarray(offset + 5, end));\n if (fragments.length > egressRuntime.maxHelloRecords)\n throw new Error(\"Too many TLS hello records\");\n offset = end;\n const hello = Buffer.concat(fragments);\n if (hello.length < 4) continue;\n if (hello[0] !== 1) throw new Error(\"TLS ClientHello required\");\n const length = hello.readUIntBE(1, 3) + 4;\n if (length > egressRuntime.maxHelloBytes)\n throw new Error(\"TLS hello too large\");\n if (hello.length < length) continue;\n const data = hello.subarray(0, length);\n let cursor = 38;\n const take = (count: number) => {\n if (count < 0 || cursor + count > data.length)\n throw new Error(\"Malformed TLS hello\");\n const part = data.subarray(cursor, cursor + count);\n cursor += count;\n return part;\n };\n take(take(1)[0]!);\n take(take(2).readUInt16BE());\n take(take(1)[0]!);\n const extensions = take(take(2).readUInt16BE());\n if (cursor !== data.length) throw new Error(\"Malformed TLS extensions\");\n let position = 0;\n let name: string | undefined;\n while (position < extensions.length) {\n if (position + 4 > extensions.length)\n throw new Error(\"Malformed TLS extension\");\n const kind = extensions.readUInt16BE(position);\n const size = extensions.readUInt16BE(position + 2);\n position += 4;\n if (position + size > extensions.length)\n throw new Error(\"Malformed TLS extension\");\n const extension = extensions.subarray(position, position + size);\n position += size;\n if (kind !== 0) continue;\n if (name !== undefined) throw new Error(\"Duplicate TLS server name\");\n if (\n size < 5 ||\n extension.readUInt16BE(0) !== size - 2 ||\n extension[2] !== 0 ||\n extension.readUInt16BE(3) !== size - 5\n )\n throw new Error(\"Malformed TLS server name\");\n name = extension.subarray(5).toString(\"utf8\");\n if (!/^[a-z0-9.-]+$/i.test(name))\n throw new Error(\"Invalid TLS server name\");\n }\n if (name !== undefined) return name.toLowerCase();\n throw new Error(\"TLS server name required\");\n }\n return undefined;\n}\n"]}
|
|
@@ -59,14 +59,13 @@ export function firecrackerCommand(runtime) {
|
|
|
59
59
|
let cancellation;
|
|
60
60
|
const cancel = () => {
|
|
61
61
|
cancellation ??= requireSuccess({
|
|
62
|
-
...firecrackerSsh(runtime.options, `umask 077; touch ${quote(id + ".cancel")}; if [ -f ${quote(id)} ]; then node -e ${quote("const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),
|
|
62
|
+
...firecrackerSsh(runtime.options, `umask 077; touch ${quote(id + ".cancel")}; if [ -f ${quote(id)} ]; then node -e ${quote("const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),500)")} ${quote(id)}; fi`),
|
|
63
63
|
deadlineMs: firecrackerDefaults.cleanupMs,
|
|
64
64
|
}, executeProcess);
|
|
65
65
|
void cancellation.catch(() => undefined);
|
|
66
66
|
};
|
|
67
67
|
signal.addEventListener("abort", cancel, { once: true });
|
|
68
68
|
const timer = setTimeout(() => stop.abort(new OutpostError("timeout", `Command exceeded ${deadlineMs} ms`)), deadlineMs);
|
|
69
|
-
let interrupted = false;
|
|
70
69
|
try {
|
|
71
70
|
const { directory: _directory, variables: _variables, ...invocation } = command;
|
|
72
71
|
const result = await runtime.executor({
|
|
@@ -84,13 +83,20 @@ export function firecrackerCommand(runtime) {
|
|
|
84
83
|
}
|
|
85
84
|
: {}),
|
|
86
85
|
signal,
|
|
87
|
-
...firecrackerSsh(runtime.options, `setsid --wait sh -c ${quote(wrapper)}`),
|
|
86
|
+
...firecrackerSsh(runtime.options, `setsid --wait sh -c ${quote(wrapper)}; status=$?; printf '%s' "$status" > ${quote(id + ".status")}; exit "$status"`),
|
|
88
87
|
});
|
|
89
88
|
signal.throwIfAborted();
|
|
89
|
+
if (result.status === 255) {
|
|
90
|
+
const status = await executeProcess({
|
|
91
|
+
...firecrackerSsh(runtime.options, `cat ${quote(id + ".status")}`),
|
|
92
|
+
deadlineMs: firecrackerDefaults.cleanupMs,
|
|
93
|
+
});
|
|
94
|
+
if (status.status !== 0 || status.stdout.trim() !== "255")
|
|
95
|
+
throw new OutpostError("provider", "Guest SSH connection failed before command completion", { unavailable: "SSH connection failure" });
|
|
96
|
+
}
|
|
90
97
|
return result;
|
|
91
98
|
}
|
|
92
99
|
catch (cause) {
|
|
93
|
-
interrupted = true;
|
|
94
100
|
try {
|
|
95
101
|
cancel();
|
|
96
102
|
await cancellation;
|
|
@@ -103,9 +109,9 @@ export function firecrackerCommand(runtime) {
|
|
|
103
109
|
finally {
|
|
104
110
|
clearTimeout(timer);
|
|
105
111
|
signal.removeEventListener("abort", cancel);
|
|
106
|
-
if (!
|
|
112
|
+
if (!runtime.isClosed())
|
|
107
113
|
await executeProcess({
|
|
108
|
-
...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)}`),
|
|
114
|
+
...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)} ${quote(id + ".cancel")} ${quote(id + ".status")}`),
|
|
109
115
|
deadlineMs: firecrackerDefaults.cleanupMs,
|
|
110
116
|
}).catch(() => undefined);
|
|
111
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firecracker-command.js","sourceRoot":"","sources":["../../src/providers/firecracker-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError, positive } from \"../domain/errors.ts\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport { firecrackerDefaults } from \"./firecracker.constants.ts\";\nimport type {\n FirecrackerOptions,\n FirecrackerRuntime,\n} from \"./firecracker.types.ts\";\n\nexport function firecrackerSsh(\n options: FirecrackerOptions,\n script: string,\n): Command {\n const ssh = options.ssh;\n return {\n executable: ssh.binary ?? (options.jailer ? \"/usr/bin/ssh\" : \"ssh\"),\n arguments: [\n \"-F\",\n \"/dev/null\",\n \"-T\",\n \"-o\",\n \"BatchMode=yes\",\n \"-o\",\n \"IdentitiesOnly=yes\",\n \"-o\",\n \"StrictHostKeyChecking=yes\",\n \"-o\",\n `UserKnownHostsFile=${ssh.knownHosts}`,\n \"-o\",\n \"GlobalKnownHostsFile=/dev/null\",\n \"-o\",\n \"ConnectTimeout=2\",\n \"-o\",\n \"ServerAliveInterval=2\",\n \"-o\",\n \"ServerAliveCountMax=2\",\n \"-i\",\n ssh.identity,\n \"-p\",\n String(ssh.port ?? 22),\n \"-l\",\n ssh.user,\n \"--\",\n ssh.host,\n script,\n ],\n };\n}\n\nexport function firecrackerCommand(\n runtime: FirecrackerRuntime,\n): SandboxLease[\"invoke\"] {\n return async (command) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Firecracker sandbox is closed\");\n invariant(\n !command.interactive && !command.elevated,\n \"Firecracker research provider does not support interactive or elevated commands\",\n );\n command.signal?.throwIfAborted();\n const variables = { ...runtime.variables, ...command.variables };\n invariant(\n Object.keys(variables).every((key) =>\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(key),\n ),\n \"Invalid environment key\",\n );\n const id = `/tmp/outpost-command-${randomUUID()}`;\n const wrapper = `umask 077; echo $$ > ${quote(id)}; test ! -e ${quote(id + \".cancel\")} || exit 130; cd ${quote(command.directory ?? runtime.root)} || exit; exec env ${Object.entries(\n variables,\n )\n .map(([key, value]) => quote(`${key}=${value}`))\n .join(\n \" \",\n )} ${[command.executable, ...(command.arguments ?? [])].map(quote).join(\" \")}`;\n const stop = new AbortController();\n const signal = command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal;\n const deadlineMs = command.deadlineMs ?? firecrackerDefaults.commandMs;\n positive(deadlineMs, \"deadlineMs\");\n let cancellation: Promise<unknown> | undefined;\n const cancel = () => {\n cancellation ??= requireSuccess(\n {\n ...firecrackerSsh(\n runtime.options,\n `umask 077; touch ${quote(id + \".cancel\")}; if [ -f ${quote(id)} ]; then node -e ${quote(\"const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),100)\")} ${quote(id)}; fi`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n },\n executeProcess,\n );\n void cancellation.catch(() => undefined);\n };\n signal.addEventListener(\"abort\", cancel, { once: true });\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", `Command exceeded ${deadlineMs} ms`),\n ),\n deadlineMs,\n );\n let interrupted = false;\n try {\n const {\n directory: _directory,\n variables: _variables,\n ...invocation\n } = command;\n const result = await runtime.executor({\n ...invocation,\n ...(command.observe\n ? {\n observe: (channel, text) => {\n try {\n command.observe!(channel, text);\n } catch (cause) {\n stop.abort(cause);\n }\n },\n }\n : {}),\n signal,\n ...firecrackerSsh(\n runtime.options,\n `setsid --wait sh -c ${quote(wrapper)}`,\n ),\n });\n signal.throwIfAborted();\n return result;\n } catch (cause) {\n interrupted = true;\n try {\n cancel();\n await cancellation;\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Guest cancellation could not be confirmed; release the VM\",\n );\n }\n throw cause;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n if (!interrupted && !runtime.isClosed())\n await executeProcess({\n ...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)}`),\n deadlineMs: firecrackerDefaults.cleanupMs,\n }).catch(() => undefined);\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"firecracker-command.js","sourceRoot":"","sources":["../../src/providers/firecracker-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGxE,OAAO,EACL,cAAc,EACd,KAAK,EACL,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMjE,MAAM,UAAU,cAAc,CAC5B,OAA2B,EAC3B,MAAc;IAEd,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;QACnE,SAAS,EAAE;YACT,IAAI;YACJ,WAAW;YACX,IAAI;YACJ,IAAI;YACJ,eAAe;YACf,IAAI;YACJ,oBAAoB;YACpB,IAAI;YACJ,2BAA2B;YAC3B,IAAI;YACJ,sBAAsB,GAAG,CAAC,UAAU,EAAE;YACtC,IAAI;YACJ,gCAAgC;YAChC,IAAI;YACJ,kBAAkB;YAClB,IAAI;YACJ,uBAAuB;YACvB,IAAI;YACJ,uBAAuB;YACvB,IAAI;YACJ,GAAG,CAAC,QAAQ;YACZ,IAAI;YACJ,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YACtB,IAAI;YACJ,GAAG,CAAC,IAAI;YACR,IAAI;YACJ,GAAG,CAAC,IAAI;YACR,MAAM;SACP;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAA2B;IAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,+BAA+B,CAAC,CAAC;QACtE,SAAS,CACP,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,QAAQ,EACzC,iFAAiF,CAClF,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACjE,SAAS,CACP,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACnC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CACrC,EACD,yBAAyB,CAC1B,CAAC;QACF,MAAM,EAAE,GAAG,wBAAwB,UAAU,EAAE,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,wBAAwB,KAAK,CAAC,EAAE,CAAC,eAAe,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,oBAAoB,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,OAAO,CACnL,SAAS,CACV;aACE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;aAC/C,IAAI,CACH,GAAG,CACJ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;YAC3B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAChD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAChB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC,SAAS,CAAC;QACvE,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACnC,IAAI,YAA0C,CAAC;QAC/C,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,YAAY,KAAK,cAAc,CAC7B;gBACE,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,oBAAoB,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC,oBAAoB,KAAK,CAAC,+QAA+Q,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM,CAC7X;gBACD,UAAU,EAAE,mBAAmB,CAAC,SAAS;aAC1C,EACD,cAAc,CACf,CAAC;YACF,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,IAAI,CAAC,KAAK,CACR,IAAI,YAAY,CAAC,SAAS,EAAE,oBAAoB,UAAU,KAAK,CAAC,CACjE,EACH,UAAU,CACX,CAAC;QACF,IAAI,CAAC;YACH,MAAM,EACJ,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,EACrB,GAAG,UAAU,EACd,GAAG,OAAO,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC;gBACpC,GAAG,UAAU;gBACb,GAAG,CAAC,OAAO,CAAC,OAAO;oBACjB,CAAC,CAAC;wBACE,OAAO,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;4BACzB,IAAI,CAAC;gCACH,OAAO,CAAC,OAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;4BAClC,CAAC;4BAAC,OAAO,KAAK,EAAE,CAAC;gCACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;4BACpB,CAAC;wBACH,CAAC;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM;gBACN,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,uBAAuB,KAAK,CAAC,OAAO,CAAC,wCAAwC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,kBAAkB,CACrH;aACF,CAAC,CAAC;YACH,MAAM,CAAC,cAAc,EAAE,CAAC;YACxB,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;oBAClC,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;oBAClE,UAAU,EAAE,mBAAmB,CAAC,SAAS;iBAC1C,CAAC,CAAC;gBACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,KAAK;oBACvD,MAAM,IAAI,YAAY,CACpB,UAAU,EACV,uDAAuD,EACvD,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAC1C,CAAC;YACN,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC;gBACT,MAAM,YAAY,CAAC;YACrB,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,MAAM,IAAI,cAAc,CACtB,CAAC,KAAK,EAAE,OAAO,CAAC,EAChB,2DAA2D,CAC5D,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACrB,MAAM,cAAc,CAAC;oBACnB,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,YAAY,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAC1E;oBACD,UAAU,EAAE,mBAAmB,CAAC,SAAS;iBAC1C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError, positive } from \"../domain/errors.ts\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport { firecrackerDefaults } from \"./firecracker.constants.ts\";\nimport type {\n FirecrackerOptions,\n FirecrackerRuntime,\n} from \"./firecracker.types.ts\";\n\nexport function firecrackerSsh(\n options: FirecrackerOptions,\n script: string,\n): Command {\n const ssh = options.ssh;\n return {\n executable: ssh.binary ?? (options.jailer ? \"/usr/bin/ssh\" : \"ssh\"),\n arguments: [\n \"-F\",\n \"/dev/null\",\n \"-T\",\n \"-o\",\n \"BatchMode=yes\",\n \"-o\",\n \"IdentitiesOnly=yes\",\n \"-o\",\n \"StrictHostKeyChecking=yes\",\n \"-o\",\n `UserKnownHostsFile=${ssh.knownHosts}`,\n \"-o\",\n \"GlobalKnownHostsFile=/dev/null\",\n \"-o\",\n \"ConnectTimeout=2\",\n \"-o\",\n \"ServerAliveInterval=2\",\n \"-o\",\n \"ServerAliveCountMax=2\",\n \"-i\",\n ssh.identity,\n \"-p\",\n String(ssh.port ?? 22),\n \"-l\",\n ssh.user,\n \"--\",\n ssh.host,\n script,\n ],\n };\n}\n\nexport function firecrackerCommand(\n runtime: FirecrackerRuntime,\n): SandboxLease[\"invoke\"] {\n return async (command) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Firecracker sandbox is closed\");\n invariant(\n !command.interactive && !command.elevated,\n \"Firecracker research provider does not support interactive or elevated commands\",\n );\n command.signal?.throwIfAborted();\n const variables = { ...runtime.variables, ...command.variables };\n invariant(\n Object.keys(variables).every((key) =>\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(key),\n ),\n \"Invalid environment key\",\n );\n const id = `/tmp/outpost-command-${randomUUID()}`;\n const wrapper = `umask 077; echo $$ > ${quote(id)}; test ! -e ${quote(id + \".cancel\")} || exit 130; cd ${quote(command.directory ?? runtime.root)} || exit; exec env ${Object.entries(\n variables,\n )\n .map(([key, value]) => quote(`${key}=${value}`))\n .join(\n \" \",\n )} ${[command.executable, ...(command.arguments ?? [])].map(quote).join(\" \")}`;\n const stop = new AbortController();\n const signal = command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal;\n const deadlineMs = command.deadlineMs ?? firecrackerDefaults.commandMs;\n positive(deadlineMs, \"deadlineMs\");\n let cancellation: Promise<unknown> | undefined;\n const cancel = () => {\n cancellation ??= requireSuccess(\n {\n ...firecrackerSsh(\n runtime.options,\n `umask 077; touch ${quote(id + \".cancel\")}; if [ -f ${quote(id)} ]; then node -e ${quote(\"const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),500)\")} ${quote(id)}; fi`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n },\n executeProcess,\n );\n void cancellation.catch(() => undefined);\n };\n signal.addEventListener(\"abort\", cancel, { once: true });\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", `Command exceeded ${deadlineMs} ms`),\n ),\n deadlineMs,\n );\n try {\n const {\n directory: _directory,\n variables: _variables,\n ...invocation\n } = command;\n const result = await runtime.executor({\n ...invocation,\n ...(command.observe\n ? {\n observe: (channel, text) => {\n try {\n command.observe!(channel, text);\n } catch (cause) {\n stop.abort(cause);\n }\n },\n }\n : {}),\n signal,\n ...firecrackerSsh(\n runtime.options,\n `setsid --wait sh -c ${quote(wrapper)}; status=$?; printf '%s' \"$status\" > ${quote(id + \".status\")}; exit \"$status\"`,\n ),\n });\n signal.throwIfAborted();\n if (result.status === 255) {\n const status = await executeProcess({\n ...firecrackerSsh(runtime.options, `cat ${quote(id + \".status\")}`),\n deadlineMs: firecrackerDefaults.cleanupMs,\n });\n if (status.status !== 0 || status.stdout.trim() !== \"255\")\n throw new OutpostError(\n \"provider\",\n \"Guest SSH connection failed before command completion\",\n { unavailable: \"SSH connection failure\" },\n );\n }\n return result;\n } catch (cause) {\n try {\n cancel();\n await cancellation;\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Guest cancellation could not be confirmed; release the VM\",\n );\n }\n throw cause;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n if (!runtime.isClosed())\n await executeProcess({\n ...firecrackerSsh(\n runtime.options,\n `rm -f -- ${quote(id)} ${quote(id + \".cancel\")} ${quote(id + \".status\")}`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n }).catch(() => undefined);\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { basename, resolve } from "node:path";
|
|
2
|
+
import { invariant, OutpostError } from "../domain/errors.js";
|
|
3
|
+
import { inside } from "../infrastructure/files.js";
|
|
4
|
+
import { copyTransfer, transferDestination, transferTarget, } from "../infrastructure/transfer-copy.js";
|
|
5
|
+
import { transfer } from "../infrastructure/transfer.js";
|
|
6
|
+
export function localFiles(root, isClosed) {
|
|
7
|
+
const copy = (source, destination, contents, options) => transfer(options, async (signal) => {
|
|
8
|
+
if (isClosed())
|
|
9
|
+
throw new OutpostError("provider", "Local sandbox is closed");
|
|
10
|
+
await transferDestination(destination);
|
|
11
|
+
if (source === destination)
|
|
12
|
+
return;
|
|
13
|
+
const target = await transferTarget(basename(source), destination, contents);
|
|
14
|
+
if (source === target)
|
|
15
|
+
return;
|
|
16
|
+
invariant(!inside(source, target), "Cannot copy a directory into itself");
|
|
17
|
+
await copyTransfer(source, target, signal);
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
upload: (source, destination, options = {}) => copy(resolve(source), resolve(root, destination), /[\\/]\.$/.test(source), options),
|
|
21
|
+
download: (source, destination, options = {}) => copy(resolve(root, source), resolve(destination), /[\\/]\.$/.test(source), options),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=local-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-files.js","sourceRoot":"","sources":["../../src/providers/local-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,cAAc,GACf,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEzD,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,QAAuB;IAEvB,MAAM,IAAI,GAAG,CACX,MAAc,EACd,WAAmB,EACnB,QAAiB,EACjB,OAAwB,EACxB,EAAE,CACF,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACjC,IAAI,QAAQ,EAAE;YACZ,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,yBAAyB,CAAC,CAAC;QAChE,MAAM,mBAAmB,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,WAAW;YAAE,OAAO;QACnC,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,QAAQ,CAAC,MAAM,CAAC,EAChB,WAAW,EACX,QAAQ,CACT,CAAC;QACF,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO;QAC9B,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,qCAAqC,CAAC,CAAC;QAC1E,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IACL,OAAO;QACL,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC5C,IAAI,CACF,OAAO,CAAC,MAAM,CAAC,EACf,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,EAC1B,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EACvB,OAAO,CACR;QACH,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC9C,IAAI,CACF,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,EACrB,OAAO,CAAC,WAAW,CAAC,EACpB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EACvB,OAAO,CACR;KACJ,CAAC;AACJ,CAAC","sourcesContent":["import { basename, resolve } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease, TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { inside } from \"../infrastructure/files.ts\";\nimport {\n copyTransfer,\n transferDestination,\n transferTarget,\n} from \"../infrastructure/transfer-copy.ts\";\nimport { transfer } from \"../infrastructure/transfer.ts\";\n\nexport function localFiles(\n root: string,\n isClosed: () => boolean,\n): Pick<SandboxLease, \"upload\" | \"download\"> {\n const copy = (\n source: string,\n destination: string,\n contents: boolean,\n options: TransferOptions,\n ) =>\n transfer(options, async (signal) => {\n if (isClosed())\n throw new OutpostError(\"provider\", \"Local sandbox is closed\");\n await transferDestination(destination);\n if (source === destination) return;\n const target = await transferTarget(\n basename(source),\n destination,\n contents,\n );\n if (source === target) return;\n invariant(!inside(source, target), \"Cannot copy a directory into itself\");\n await copyTransfer(source, target, signal);\n });\n return {\n upload: (source, destination, options = {}) =>\n copy(\n resolve(source),\n resolve(root, destination),\n /[\\\\/]\\.$/.test(source),\n options,\n ),\n download: (source, destination, options = {}) =>\n copy(\n resolve(root, source),\n resolve(destination),\n /[\\\\/]\\.$/.test(source),\n options,\n ),\n };\n}\n"]}
|