feat(world-aws): AWS World implementation for Workflow DevKit#21
feat(world-aws): AWS World implementation for Workflow DevKit#21stewartjarod wants to merge 20 commits into
Conversation
… SQS) Implements @wraps.dev/world-aws package providing Storage, Queue, and Streamer backed by DynamoDB and SQS. Enables workflow execution on users' own AWS accounts with zero stored credentials. Storage: 6 DynamoDB tables (runs, steps, events, hooks, waits, streams) Queue: SQS Standard with DLQs, Lambda adapter with partial batch failure Streamer: DynamoDB chunks with monotonic ULID ordering Setup: idempotent bin/setup.ts for table and queue creation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace polling-based readFromStream() with DynamoDB Streams change data capture. Two-phase approach: catch up from table, then subscribe to INSERT events via GetRecords. Deduplicates overlap, filters by streamId, handles shard expiry and exhaustion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ion, AbortSignal - Fix deleteHooksAndWaitsForRun() to paginate through all results - Add real idempotency to step_started/completed/failed handlers - Extract shared marshal functions into storage/marshal.ts - Make queue URL construction lazy with AWS_ACCOUNT_ID validation - Add AbortSignal support to readFromStream() for cancellation - Add 11 new tests covering pagination, idempotency, streams, queue - Fix lint issues: interface→type, formatting, export patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Per-run AES-256 key derivation via Web Crypto HKDF from a base key in WORKFLOW_AWS_ENCRYPTION_KEY env var. When set, workflow core encrypts all run data (inputs, outputs, step results) with AES-256-GCM. When absent, encryption is disabled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n, JSDoc Add WorldError with throttling/credential detection at storage boundaries, validate encryption key format at config time, wire AbortController through close() to cancel active stream reads, and document public API with JSDoc. Tests: pagination edge cases, error utilities, config validation, encryption determinism (135 total). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm strips bin entries with ./ prefix paths (npm/cli#7302). Fix by using a thin bin/ wrapper and letting npm pkg fix normalize paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… handler Add encryption configuration and HKDF-SHA256 docs to README, SQS Lambda handler to Next.js example, and Lambda handler section to Next.js README. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Architecture overview with event flow diagram, table schemas, queue config, and streaming internals. Local development with LocalStack and DynamoDB Local. Migration guide from Vercel World with comparison table and zero-code-change setup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the queue handler returns { timeoutSeconds }, re-deliver the
message after a delay instead of silently dropping it. Accepts an
optional onTimeout callback for custom scheduling (e.g. EventBridge);
defaults to SQS re-queue with DelaySeconds capped at 900s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make handleHookCreated atomic with TransactWriteCommand, wrap deleteHooksAndWaitsForRun with wrapAWSError, parallelize batch deletes and SQS record processing, refactor list/listByCorrelationId to build QueryCommand params inline, add batchWriteWithRetry helper for UnprocessedItems, remove unused DLQ URL variables in setup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Duration type + computeTTL helper, threaded through config, storage, and streamer. Setup enables TTL on all tables. Items without the ttl attribute are unaffected (opt-in via config.ttl or WORKFLOW_AWS_TTL). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ization - Add world-aws-poll CLI for local development (polls SQS, forwards to local HTTP) - Add CJS output format alongside ESM, fix export condition ordering - Add retry/timeout config to all AWS SDK clients (maxAttempts: 5) - Fix Date serialization: convert Date objects to ISO strings before DynamoDB storage - Fix resumeAt deserialization: convert ISO string back to Date on read - Fix DynamoDB reserved word conflict: alias `output` in UpdateExpression - Fix SQS queue existence check for both error name variants - Add cursor validation with proper error messages - Bump to 0.2.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…poller, unit tests - Add dual-protocol support to createQueueHandler (header-based for local dev + body envelope for Lambda SQS) - Add SQS poller to start() for local dev/test bridging - Wire up @workflow/world-testing e2e suite with per-run queue isolation - Add unit tests for util, tables, and marshal modules - Fix lint/formatting across package Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…script) SST dev infra wires SQS → Lambda → local dev server for full-path e2e testing against the workflow devkit test suite. Non-production stages only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clean split: "Automations" = no-code visual builder (platform), "Workflows" = code-first durable execution (world-aws SDK). DB, API, frontend, CLI, and tests renamed. SQL table names kept as-is (aliased at Drizzle level). Old files re-export from new locations for backward compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-fix import sorting and formatting from rename. Delete old workflow-builder node/edge/test files that were duplicated (not shimmed) into automation-builder — fixes baseline ratchet overages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| const response = await fetch(url, { | ||
| method, | ||
| headers: baseHeaders, | ||
| body: fetchBody, | ||
| }); |
Check failure
Code scanning / CodeQL
Server-side request forgery Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 months ago
In general, to fix this problem, we should ensure that user-controlled data used in request URLs is strictly validated and/or encoded, especially when used as a path segment. We want to prevent characters that could alter the URL structure (like /, \, ?, #) or inject additional path elements, while preserving the existing functionality of referencing automations by their IDs.
The best approach here is to introduce a small, local validator for automation IDs (for example, restricting them to a sane character set such as alphanumerics, dashes, and underscores) and use its sanitized / validated result when constructing the URL. If the provided automationId does not match the allowed pattern, we can fail early and return an error result instead of issuing the HTTP request. This keeps the hostname and base path unchanged, prevents any path traversal or malformed ID injection, and minimally impacts behavior for legitimate IDs.
Concretely, within apps/web/src/actions/automations.ts:
- Add a helper function near
callAutomationScheduleApi(or above it) such assanitizeAutomationIdorisValidAutomationIdthat:- Checks that the ID is non-empty and matches a safe regex, e.g.
/^[A-Za-z0-9_-]{1,128}$/. - Returns a boolean or throws/returns an error string.
- Checks that the ID is non-empty and matches a safe regex, e.g.
- In
callAutomationScheduleApi, before constructingurl, validateautomationIdusing that helper.- If invalid, log (optionally) and return
{ success: false, error: "Invalid automation ID" }without callingfetch. - If valid, use the original
automationId(the same string) when building the URL, since we've ensured it can't contain dangerous characters.
- If invalid, log (optionally) and return
- Because all the tainted flows (from
updateAutomation,deleteAutomation,enableAutomation,disableAutomation) share the same sink (callAutomationScheduleApi), validating in this one place will fix all four alert variants without changing the public function signatures.
No extra imports are strictly required; this can be done with basic TypeScript and a regex.
| @@ -146,6 +146,15 @@ | ||
| // ═══════════════════════════════════════════════════════════════════════════ | ||
|
|
||
| /** | ||
| * Validate that an automation ID is safe to use in a URL path segment. | ||
| * Only allow a restricted character set to avoid path manipulation. | ||
| */ | ||
| function isValidAutomationId(automationId: string): boolean { | ||
| // Allow common ID characters (alphanumeric, dash, underscore) and cap length. | ||
| return /^[A-Za-z0-9_-]{1,128}$/.test(automationId); | ||
| } | ||
|
|
||
| /** | ||
| * Call the automation schedule API to manage EventBridge schedules. | ||
| * Follows the same pattern as batch.ts for auth + org headers. | ||
| */ | ||
| @@ -155,6 +164,13 @@ | ||
| action: "enable" | "disable" | "update", | ||
| body?: { cronExpression: string; timezone?: string } | ||
| ): Promise<{ success: boolean; error?: string }> { | ||
| if (!isValidAutomationId(automationId)) { | ||
| console.error( | ||
| `[automation-schedule] Invalid automationId provided: ${automationId}` | ||
| ); | ||
| return { success: false, error: "Invalid automation ID" }; | ||
| } | ||
|
|
||
| const apiUrl = process.env.NEXT_PUBLIC_API_URL; | ||
| if (!apiUrl) { | ||
| console.error("[automation-schedule] NEXT_PUBLIC_API_URL not configured"); |
| return { success: true }; | ||
| } catch (error) { | ||
| console.error( | ||
| `[automation-schedule] API ${action} error for ${automationId}:`, |
Check failure
Code scanning / CodeQL
Use of externally-controlled format string High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 months ago
General fix: Avoid letting untrusted data influence the format string of logging/formatting functions. Either (a) place untrusted data into subsequent arguments while using %s placeholders in a constant format string, or (b) avoid format-string-style logging entirely and log structured data (objects) or concatenate strings safely.
Best fix here: We can keep the existing logging behavior but move all dynamic/tainted values into a separate argument rather than interpolating them into the first string. For example, instead of:
console.error(
`[automation-schedule] API ${action} error for ${automationId}:`,
error
);we can log a constant string as the first argument and pass details as an object:
console.error(
"[automation-schedule] API error",
{ action, automationId, error }
);This removes any dependency of the format string on untrusted input (automationId) and also addresses all CodeQL variants associated with the same call site (no matter whether automationId came from updateAutomation, deleteAutomation, enableAutomation, or disableAutomation). This change preserves functionality (we still log the same conceptual information, arguably more clearly) and requires no new imports or helpers, and is confined to the provided snippet in apps/web/src/actions/automations.ts.
Specific location:
- File:
apps/web/src/actions/automations.ts - Function:
callAutomationScheduleApi - Lines: 213–217, replacing the
console.errorcall to avoid a template literal that embedsaction/automationId.
No additional methods or imports are needed.
| @@ -212,8 +212,8 @@ | ||
| return { success: true }; | ||
| } catch (error) { | ||
| console.error( | ||
| `[automation-schedule] API ${action} error for ${automationId}:`, | ||
| error | ||
| "[automation-schedule] API error", | ||
| { action, automationId, error } | ||
| ); | ||
| return { | ||
| success: false, |
Complete the rename inside automation-builder components, store, tests, and API workers. Rename page components and route segment [workflowId] → [automationId]. Update toast messages and CLAUDE.md docs to reflect new paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API tests: update mock paths (workflow-queue→automation-queue, workflow-scheduler→automation-scheduler), add automation table exports to @wraps/db mocks, fix error message expectations. Rename 3 DB-dependent tests to .integration.test.ts to match exclusion pattern. Auth tests: skip Stripe config assertions when env vars are missing, fix conditional Stripe plugin test to handle both cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
@wraps.dev/world-awspackage — AWS-backed World implementation for Vercel Workflow DevKit using DynamoDB (storage + streams) and SQS (queue)createSQSHandler()with partial batch failure reporting and configurable timeout/sleep handlingstart()automatically bridges SQS to local HTTP server whenPORTorWORKFLOW_LOCAL_BASE_URLis setworld-aws-setupbin for creating DynamoDB tables and SQS queues@workflow/world-testingsuite (addition, idempotency, hooks, null bytes, error handling) with per-run queue isolationTest plan
cd packages/world-aws && pnpm test— 214 unit tests pass (no AWS needed)AWS_REGION=us-east-1 AWS_ACCOUNT_ID=<id> pnpm test:world— 6 e2e tests pass (requires SST dev running + AWS credentials)pnpm build— package compiles to ESM + CJS with type declarationspnpm typecheck— no type errors🤖 Generated with Claude Code