fix: add future timestamp rejection to webhook replay protection#83
Open
jonwiggins wants to merge 8 commits intomainfrom
Open
fix: add future timestamp rejection to webhook replay protection#83jonwiggins wants to merge 8 commits intomainfrom
jonwiggins wants to merge 8 commits intomainfrom
Conversation
Add signature verification to the /api/webhooks/github endpoint to prevent forged webhook payloads. When GITHUB_WEBHOOK_SECRET is configured, all incoming webhooks must include a valid X-Hub-Signature-256 header. Also adds replay protection to reject events with stale timestamps. - Validate HMAC-SHA256 signature against raw request body using timing-safe comparison - Return 401 for missing or invalid signatures - Add replay protection (reject events older than 5 minutes) - Use Fastify preParsing hook to capture raw body for signature verification - Add /api/webhooks/ to public routes (webhooks use signature auth, not session cookies) - Unit tests for signature verification and replay detection - Updated .env.example and Helm chart with GITHUB_WEBHOOK_SECRET config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add indexes on frequently queried columns to prevent full table scans as task volume grows: - tasks(repo_url, state) — filtering tasks by repo and state - tasks(state) — state-based filtering on task list - tasks(parent_task_id) — finding subtasks - tasks(created_at DESC) — sorting by creation time - task_logs(task_id, timestamp) — fetching logs for a task - repo_pods(repo_url) — finding pods by repo - task_events(task_id) — fetching event history Includes both Drizzle schema index definitions and a SQL migration. Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
) Add a webhook notification system that delivers events to external endpoints when task state changes occur. Supports Slack incoming webhooks out of the box with Block Kit formatted messages. - CRUD endpoints: POST/GET/DELETE /api/webhooks - Delivery log: GET /api/webhooks/:id/deliveries - Events: task.completed, task.failed, task.needs_attention, task.pr_opened, review.completed - HMAC-SHA256 signature in X-Optio-Signature header - BullMQ worker with exponential backoff retry (3 attempts) - Auto-detect Slack webhook URLs and format payloads with blocks - Webhook secrets masked in API responses Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the Notion ticket provider stub that threw "not yet implemented" errors on all methods. This prevents crashes if a user configures Notion as a ticket source. The TicketSource enum, provider factory, and docs are updated to reflect only the implemented providers (GitHub, Linear). Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add production-ready features to the Helm chart: - Ingress TLS with cert-manager integration: auto-generates TLS blocks and cluster-issuer annotations when certManager.enabled is set - Namespace ResourceQuota: configurable CPU, memory, pod, and PVC limits to prevent resource exhaustion by rogue agents - Pod anti-affinity for API and web: soft/hard modes to spread replicas across nodes for high availability - Agent PVC template: ConfigMap-based template for repo pod home directory PVCs with configurable storage class and size - Image pull secrets: global imagePullSecrets support for private registries - Secure defaults: empty postgres password (required when auth enabled), chart fails fast with a clear error message - RBAC: added PVC and ConfigMap permissions for API server - Worker scaling documentation: explains BullMQ's Redis-backed job delivery ensures safe multi-replica API scaling Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add test coverage reporting infrastructure - Install @vitest/coverage-v8 in all test packages (api, shared, agent-adapters) - Add vitest.config.ts with v8 coverage provider and json-summary reporter - Add test:coverage scripts to package.json in each test package - Add test:coverage turbo task with coverage output caching - Add coverage report generation script (scripts/coverage-report.mjs) - Generates markdown table with per-package coverage metrics - Writes to coverage-report.md for PR comments - Writes to GITHUB_STEP_SUMMARY when available - Add coverage/ and coverage-report.md to .gitignore - Add eslint as explicit root devDependency (needed by lint-staged) Note: CI workflow update (.github/workflows/ci.yml) requires a GitHub token with `workflow` scope. The required change is to replace `pnpm turbo test` with `pnpm turbo test:coverage` and add coverage report + artifact upload steps. See PR description for the exact diff. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add coverage thresholds and CI workflow integration - Add minimum coverage thresholds to vitest configs: - api: 25% lines/statements/functions, 50% branches - shared: 70% lines/statements/functions, 80% branches - agent-adapters: 50% lines/statements, 80% branches/functions - Update CI test job to run test:coverage with PR comment reporting - Add coverage artifact upload with 14-day retention - Coverage report uses marocchino/sticky-pull-request-comment for PR comments Note: CI workflow changes (.github/workflows/ci.yml) require a PAT with the `workflow` scope. The workflow changes are included in this commit and will need to be pushed by a token with appropriate permissions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: auto-enable coverage in CI via vitest config Set coverage.enabled = !!process.env.CI in all vitest configs so that `pnpm turbo test` automatically collects coverage data and enforces thresholds when running in GitHub Actions (CI=true), while keeping local test runs fast by default. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add task search and filtering with saved views Add server-side search API (GET /api/tasks/search) with full-text search on title/prompt, multi-field filtering (state, repo, agent type, cost range, date range, author), and cursor-based pagination. Update the web UI task list with an advanced filters panel, cost range inputs, saved views persisted in localStorage, URL-synced filters for shareable links, and a "Load more" button for cursor pagination. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add task search and filtering with cursor-based pagination Add server-side search API (GET /api/tasks/search) with full-text search on title/prompt, multi-field filtering (state, repo, agent type, cost range, date range, author), and cursor-based pagination. Update the web UI with advanced filter panel (repo dropdown, agent type, cost range), URL-synced filters for shareable links, saved views in localStorage, and a load-more button for paginated results. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The existing isReplayedEvent() only rejects events with timestamps in the past. This adds rejection of events with timestamps far in the future, which mitigates clock-skew replay attacks where an attacker sends a replayed event with a manipulated future timestamp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
isReplayedEvent()webhook replay protection to also reject events with timestamps far in the futureContext
The webhook signature validation (merged in #76) included replay protection that rejects events older than 5 minutes. However, it did not reject events with timestamps in the future, leaving a potential attack vector for clock-skew replay attacks.
Test plan
isReplayedEventtests still pass🤖 Generated with Claude Code