Skip to content

feat: add Adrapid plugin - #653

Closed
Vishuzz wants to merge 1 commit into
corsairdev:mainfrom
Vishuzz:feat/adrapid-plugin
Closed

feat: add Adrapid plugin#653
Vishuzz wants to merge 1 commit into
corsairdev:mainfrom
Vishuzz:feat/adrapid-plugin

Conversation

@Vishuzz

@Vishuzz Vishuzz commented Aug 10, 2026

Copy link
Copy Markdown

What changed

The youcom plugin was already implemented under packages/youcom/, but it was missing from the core provider registry.

I registered it in packages/corsair/core/constants.ts by:

  • Adding youcom to BaseProviders
  • Adding youcom: 'You.com' to ProviderDisplayNames
  • Adding youcom to the AllProviders type

I also updated packages/youcom/jest.config.cjs with the required module mappings for corsair/core and corsair/hub.

Verification

All checks pass:

  • pnpm run validate:plugins
  • pnpm typecheck
  • pnpm lint
  • pnpm --filter @corsair-dev/youcom test — 22/22 tests passing

Summary by CodeRabbit

  • New Features
    • Added support for the Adrapid provider, including API access, authentication, endpoint handling, and webhook processing.
    • Added example Adrapid endpoint and webhook integrations with request validation and tenant matching.
    • Added OAuth-based tenant linking and configurable error handling for authentication and rate-limit responses.
    • Added Adrapid and You.com to the supported provider list.
  • Bug Fixes
    • Removed an unused Gmail integration from the demo configuration.

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@Vishuzz is attempting to deploy a commit to the corsair Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added app App / Hub-facing app code core Changes in packages/corsair labels Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds the @corsair-dev/adrapid plugin with typed API endpoints, webhooks, authentication, tenant matching, schemas, packaging, and tests. It registers Adrapid and You.com providers and removes two unused imports.

Changes

Adrapid plugin

Layer / File(s) Summary
Adrapid contracts and request handling
packages/adrapid/schema/*, packages/adrapid/endpoints/*, packages/adrapid/client.ts, packages/adrapid/error-handlers.ts
Adds typed endpoint schemas, a generic API client, rate-limit and authentication error handlers, and the example GET endpoint.
Adrapid webhook processing
packages/adrapid/webhooks/*
Adds webhook payload parsing, event matching, signature verification, tenant matching, OAuth tenant linking, and the example webhook.
Adrapid plugin registration
packages/adrapid/index.ts
Registers endpoints and webhooks, defines authentication metadata, resolves keys, configures tenant hooks, and exports public types.
Adrapid package and schema support
packages/adrapid/package.json, packages/adrapid/tsconfig.json, packages/adrapid/tsup.config.ts, packages/adrapid/jest.config.cjs, packages/adrapid/schema.test.ts
Adds package, build, TypeScript, Jest, and schema validation configuration.

Provider registration

Layer / File(s) Summary
Provider identifiers and test aliases
packages/corsair/core/constants.ts, packages/youcom/jest.config.cjs
Registers Adrapid and You.com in provider lists, display names, and unions. Adds You.com Jest aliases.

Import cleanup

Layer / File(s) Summary
Unused import removal
demo/mcp/corsair.ts, www/src/components/landing/menu/site-menu.tsx
Removes the Gmail plugin import and the unused ArrowRightIcon import.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AdrapidPlugin
  participant exampleGet
  participant makeAdrapidRequest
  participant AdrapidAPI
  Caller->>AdrapidPlugin: invoke registered endpoint
  AdrapidPlugin->>exampleGet: validate input and run handler
  exampleGet->>makeAdrapidRequest: send id and API key
  makeAdrapidRequest->>AdrapidAPI: issue configured GET request
  AdrapidAPI-->>makeAdrapidRequest: return response or error
  makeAdrapidRequest-->>exampleGet: return typed result
  exampleGet-->>Caller: log completion and return response
Loading

Possibly related issues

  • corsairdev/corsair issue 622 — Registers the You.com provider in the same provider constants updated by this PR.

Possibly related PRs

  • corsairdev/corsair#327 — Adds a structurally similar provider plugin with API client, typed endpoints, authentication, and webhooks.
  • corsairdev/corsair#648 — Adds a parallel provider integration with matching client, endpoint, webhook, schema, and registration patterns.
  • corsairdev/corsair#650 — Adds a similar provider plugin with near-identical integration components.

Suggested labels: plugin

Suggested reviewers: devjain32

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary changes, which add the Adrapid plugin and its supporting package modules.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Warning

⚠️ This pull request has been flagged as potential spam (gibberish) by CodeRabbit slop detection and should be reviewed carefully.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a generated Adrapid plugin package and also registers You.com in the core provider vocabulary. The Adrapid implementation is not production-ready and the unrelated You.com, demo, and website edits violate the enforced single-plugin PR boundary.

  • Adds an Adrapid client, example endpoint, schemas, error handlers, auth configuration, and webhook plumbing.
  • Registers Adrapid and You.com provider IDs and display names.
  • Updates You.com Jest aliases and performs unrelated demo and website import cleanup.
  • Leaves the Adrapid API host, webhook verification, endpoint tests, and several provider-specific contracts as generator placeholders.

Confidence Score: 0/5

This PR is not safe to merge until the scope violations, placeholder Adrapid implementation, missing endpoint coverage, and unauthenticated webhook path are fixed.

The deterministic plugin gate rejects the changed-file footprint, the only API endpoint targets a placeholder host, and the webhook handler accepts forged events because signature verification always succeeds.

Files Needing Attention: packages/adrapid/client.ts, packages/adrapid/webhooks/types.ts, packages/adrapid/endpoints/example.ts, packages/adrapid/schema.test.ts, packages/youcom/jest.config.cjs, demo/mcp/corsair.ts, www/src/components/landing/menu/site-menu.tsx

Security Review

The Adrapid webhook verifier always succeeds. Requests only need to include the expected header name to reach a handler that accepts and logs the event without validating possession of the webhook secret.

Important Files Changed

Filename Overview
packages/adrapid/client.ts Adds the plugin HTTP client but retains api.example.com, so the only endpoint cannot contact Adrapid.
packages/adrapid/index.ts Wires auth, endpoint, and webhook configuration, but provider matching trusts signature-header presence and several provider-specific fields remain placeholders.
packages/adrapid/webhooks/types.ts Defines webhook schemas and matching helpers but unconditionally accepts every webhook signature.
packages/adrapid/endpoints/example.ts Adds a generated example endpoint that targets the placeholder client and has no corresponding endpoint test.
packages/adrapid/schema.test.ts Tests only schema metadata and does not exercise the implemented endpoint or webhook behavior.
packages/corsair/core/constants.ts Consistently registers Adrapid and You.com in provider IDs, display names, and the provider union.
packages/youcom/jest.config.cjs Adds useful core and hub aliases, but changing a second plugin makes this Adrapid PR fail the scope gate.
demo/mcp/corsair.ts Removes an unused Gmail import, but this unrelated edit is prohibited in a plugin PR.
www/src/components/landing/menu/site-menu.tsx Removes an unused icon import, but this unrelated website edit is prohibited in a plugin PR.

Sequence Diagram

sequenceDiagram
    participant Attacker
    participant Router as Webhook Router
    participant Matcher as Adrapid Matchers
    participant Handler as Example Handler
    participant Events as Corsair Events
    Attacker->>Router: x-adrapid-signature + example payload
    Router->>Matcher: Identify provider and tenant
    Matcher-->>Router: Match from header/body
    Router->>Handler: Dispatch parsed payload
    Handler->>Handler: verifyAdrapidWebhookSignature()
    Note over Handler: Always returns valid: true
    Handler->>Events: Log completed event
    Handler-->>Attacker: success: true
Loading

Reviews (1): Last reviewed commit: "feat: add Adrapid plugin" | Re-trigger Greptile

Comment on lines +47 to +48
'^corsair/core$': '<rootDir>/../corsair/core.ts',
'^corsair/hub$': '<rootDir>/../corsair/hub.ts',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Plugin scope gate is broken

This Adrapid PR also changes the You.com plugin, a demo file, and a website file. The deterministic plugin gate treats this as both a multi-plugin change and an out-of-scope change, causing the required check to fail and block the PR from merging.

Rule Used: A plugin PR must only modify files inside a single... (source)

Knowledge Base Used: The provider-plugin package pattern

}

// TODO: Update with your API base URL
const ADRAPID_API_BASE = 'https://api.example.com';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Endpoint uses placeholder API host

When a consumer invokes adrapid.api.example.get, the client sends the request to https://api.example.com rather than an Adrapid API. The plugin's only endpoint therefore fails instead of returning the requested provider resource.

Rule Used: Flag boilerplate residue from the plugin generator... (source)

Knowledge Base Used: The provider-plugin package pattern

secret: string,
): { valid: boolean; error?: string } {
// TODO: Implement webhook signature verification
return { valid: true };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Webhook authentication always succeeds

When a request supplies the expected header and an example payload, this verifier ignores both the request and secret and returns valid: true, causing an unauthenticated event to be accepted and logged as a completed Adrapid webhook. How this was verified: The request path checks only for header presence before dispatch, and the handler relies on this unconditional result before logging the event.

Rule Used: Flag boilerplate residue from the plugin generator... (source)

Knowledge Base Used: The provider-plugin package pattern

Comment on lines +7 to +10
const response = await makeAdrapidRequest<
AdrapidEndpointOutputs['exampleGet']
>(`example/${input.id}`, ctx.key, { method: 'GET' });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Endpoint has no behavioral test

The package's only test checks schema metadata and never invokes this implemented endpoint. As a result, the placeholder host and request behavior pass the package test suite without any endpoint assertion, contrary to the required plugin test coverage.

Rule Used: Plugin packages must include at least one *.test.t... (source)

Knowledge Base Used: The provider-plugin package pattern

@github-actions

Copy link
Copy Markdown

Plugin PR scorecard — packages/adrapid

Check Status Notes
R1 — Scope: one plugin per PR This PR touches: adrapid, youcom
R2 — Tests with assertions
R3 — Description Description section is empty or placeholder
R3 — Linked issue / claim ⚠️ No "Fixes #…" or claim link — add one if this PR has a claim or issue
R4 — Demo video / recording Required in "Screenshots / Demos" before a maintainer reviews

Rules: PLUGIN_PR_RULES.md · re-runs on every push

@github-actions github-actions Bot added the gate:failed Plugin PR gate checks failing label Aug 10, 2026
@github-actions

Copy link
Copy Markdown

Hey @Vishuzz, thanks for the contribution! 🏴‍☠️ Before a maintainer reviews, please fix the items below — the review re-runs automatically on your next push.

Must fix

  • P0 packages/youcom/jest.config.cjs:48Plugin scope gate is broken
    This Adrapid PR also changes the You.com plugin, a demo file, and a website file. The deterministic plugin gate treats this as both a multi-plugin change and an out-of-scope change, causing the required check to fail and block the PR from merging.

Rule Used: A plugin PR must only modify files inside a single... (source)

Knowledge Base Used: The provider-plugin package pattern

  • P1 packages/adrapid/client.ts:15Endpoint uses placeholder API host
    When a consumer invokes adrapid.api.example.get, the client sends the request to https://api.example.com rather than an Adrapid API. The plugin's only endpoint therefore fails instead of returning the requested provider resource.

Rule Used: Flag boilerplate residue from the plugin generator... (source)

Knowledge Base Used: The provider-plugin package pattern

  • P1 packages/adrapid/webhooks/types.ts:61Webhook authentication always succeeds
    When a request supplies the expected header and an example payload, this verifier ignores both the request and secret and returns valid: true, causing an unauthenticated event to be accepted and logged as a completed Adrapid webhook. How this was verified: The request path checks only for header presence before dispatch, and the handler relies on this unconditional result before logging the event.

Rule Used: Flag boilerplate residue from the plugin generator... (source)

Knowledge Base Used: The provider-plugin package pattern

  • P1 packages/adrapid/endpoints/example.ts:10Endpoint has no behavioral test
    The package's only test checks schema metadata and never invokes this implemented endpoint. As a result, the placeholder host and request behavior pass the package test suite without any endpoint assertion, contrary to the required plugin test coverage.

Rule Used: Plugin packages must include at least one *.test.t... (source)

Knowledge Base Used: The provider-plugin package pattern

PR requirements (rules)

  • R1 — This PR touches: adrapid, youcom
  • R3 — Description section is empty or placeholder
  • R4 — Required in "Screenshots / Demos" before a maintainer reviews

If anything remains after your next push, a bot commit will clean it up; a maintainer always does the final review and merge.

@github-actions github-actions Bot added the bot:round-1 Review bot posted consolidated findings label Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/adrapid/client.ts`:
- Around line 14-15: Replace the placeholder value of ADRAPID_API_BASE with the
documented Adrapid API base URL, removing the example endpoint before plugin
registration.
- Around line 54-58: Update the ADRAPID_API_BASE constant to the actual Adrapid
API base URL instead of the placeholder. In the error handling around the client
request, re-throw existing ApiError instances before wrapping other Error
values, preserving their status and retryAfter metadata; retain the
unknown-error fallback for non-Error values.

In `@packages/adrapid/endpoints/example.ts`:
- Around line 4-17: Validate the response in the get endpoint using
AdrapidEndpointOutputSchemas.exampleGet immediately after makeAdrapidRequest and
before logEventFromContext. Use the parsed result as the returned response, so
malformed provider data is rejected before consumers receive it or the completed
event is logged.

In `@packages/adrapid/index.ts`:
- Around line 115-122: Determine AdRapid’s stable account identifier and use
that single link type consistently: update adrapidAuthConfig in
packages/adrapid/index.ts (115-122), the webhook matcher in
packages/adrapid/webhooks/tenant-matcher.ts (17-24), and the OAuth resolver in
packages/adrapid/webhooks/oauth-tenant-link.ts (9-30). Ensure OAuth tokens
resolve and store the webhook link using the provider’s webhook
data.id-compatible identifier, and remove the placeholder URL and TODO stubs.

In `@packages/adrapid/webhooks/types.ts`:
- Around line 56-62: The verifyAdrapidWebhookSignature function must perform
real authentication instead of always returning valid. Implement the configured
signature scheme using request.rawBody and the provided secret, reject missing
or malformed signatures or secrets, and compare HMAC signatures with a
timing-safe method; add tests covering valid, invalid, missing, and malformed
signatures.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f250a97f-ebea-4af0-a362-a6c4277d2359

📥 Commits

Reviewing files that changed from the base of the PR and between 99ade55 and 0eb1671.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • demo/mcp/corsair.ts
  • packages/adrapid/client.ts
  • packages/adrapid/endpoints/example.ts
  • packages/adrapid/endpoints/index.ts
  • packages/adrapid/endpoints/types.ts
  • packages/adrapid/error-handlers.ts
  • packages/adrapid/index.ts
  • packages/adrapid/jest.config.cjs
  • packages/adrapid/package.json
  • packages/adrapid/schema.test.ts
  • packages/adrapid/schema/database.ts
  • packages/adrapid/schema/index.ts
  • packages/adrapid/tsconfig.json
  • packages/adrapid/tsup.config.ts
  • packages/adrapid/webhooks/example.ts
  • packages/adrapid/webhooks/index.ts
  • packages/adrapid/webhooks/oauth-tenant-link.ts
  • packages/adrapid/webhooks/tenant-matcher.ts
  • packages/adrapid/webhooks/types.ts
  • packages/corsair/core/constants.ts
  • packages/youcom/jest.config.cjs
  • www/src/components/landing/menu/site-menu.tsx
💤 Files with no reviewable changes (1)
  • demo/mcp/corsair.ts

Comment on lines +14 to +15
// TODO: Update with your API base URL
const ADRAPID_API_BASE = 'https://api.example.com';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Replace the placeholder API base URL before release.

Every endpoint request uses https://api.example.com. The Adrapid endpoint cannot reach the provider with this value. Configure the documented Adrapid API base URL before registering this plugin.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adrapid/client.ts` around lines 14 - 15, Replace the placeholder
value of ADRAPID_API_BASE with the documented Adrapid API base URL, removing the
example endpoint before plugin registration.

Comment on lines +54 to +58
} catch (error) {
if (error instanceof Error) {
throw new AdrapidAPIError(error.message);
}
throw new AdrapidAPIError('Unknown error');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline packages/adrapid --items all --type class,function
rg -n -C 5 'makeAdrapidRequest|instanceof ApiError|retryAfter|errorHandlers' packages/adrapid

Repository: corsairdev/corsair

Length of output: 6371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- packages/adrapid/client.ts ---'
cat -n packages/adrapid/client.ts

printf '%s\n' '--- packages/adrapid/error-handlers.ts ---'
cat -n packages/adrapid/error-handlers.ts

printf '%s\n' '--- ApiError definition and request implementation ---'
rg -n -C 8 'class ApiError|export class ApiError|function request|const request|retryAfter' packages --glob '*.{ts,tsx,js,jsx}'

Repository: corsairdev/corsair

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Adrapid client ---'
sed -n '1,110p' packages/adrapid/client.ts

printf '%s\n' '--- Adrapid handlers ---'
sed -n '1,80p' packages/adrapid/error-handlers.ts

printf '%s\n' '--- Core ApiError/request locations ---'
rg -l 'export class ApiError|class ApiError|export async function request|async function request' packages/corsair --glob '*.{ts,tsx}' \
  | head -n 30

printf '%s\n' '--- Core ApiError/request definitions ---'
rg -n -C 12 'export class ApiError|class ApiError|export async function request|async function request' packages/corsair --glob '*.{ts,tsx}' \
  | head -n 240

Repository: corsairdev/corsair

Length of output: 4514


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- ApiError implementation ---'
sed -n '100,180p' packages/corsair/async-core/ApiError.ts

printf '%s\n' '--- corsair/http exports ---'
rg -n -C 5 'ApiError|request' packages/corsair --glob 'index.ts' --glob '*http*' --glob '*request*' \
  | head -n 240

printf '%s\n' '--- request call and error construction ---'
rg -n -C 8 'new ApiError|request<|request\(' packages/corsair/async-core packages/corsair/http --glob '*.{ts,tsx}' \
  | head -n 300

Repository: corsairdev/corsair

Length of output: 19300


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- corsair/http export path ---'
fd -i 'http|index.ts|package.json' packages/corsair | head -n 120
rg -n -C 4 'async-core/(ApiError|request)|from .*ApiError|export .*ApiError|corsair/http' packages/corsair packages/adrapid \
  --glob '*.{ts,tsx,json}' | head -n 220

printf '%s\n' '--- Static behavior check ---'
python3 - <<'PY'
from pathlib import Path

client = Path("packages/adrapid/client.ts").read_text()
handlers = Path("packages/adrapid/error-handlers.ts").read_text()
request = Path("packages/corsair/async-core/request.ts").read_text()
api_error = Path("packages/corsair/async-core/ApiError.ts").read_text()

checks = {
    "client wraps Error": "throw new AdrapidAPIError(error.message)" in client,
    "client does not import ApiError": "ApiError" not in client.split("export async function", 1)[0],
    "ApiError exposes status": "public readonly status: number;" in api_error,
    "ApiError exposes retryAfter": "public readonly retryAfter?: number;" in api_error,
    "request throws ApiError": "throw new ApiError(options, result" in request,
    "rate handler checks ApiError": "error instanceof ApiError && error.status === 429" in handlers,
    "rate handler reads retryAfter": "error instanceof ApiError && error.retryAfter !== undefined" in handlers,
    "auth handler checks ApiError": "error instanceof ApiError && error.status === 401" in handlers,
    "placeholder base URL remains": "https://api.example.com" in client,
}

for name, passed in checks.items():
    print(f"{name}: {'yes' if passed else 'no'}")
PY

Repository: corsairdev/corsair

Length of output: 11618


Use the Adrapid API URL and preserve ApiError metadata.

  • Replace ADRAPID_API_BASE = 'https://api.example.com' with the Adrapid API base URL. Current requests target a placeholder.
  • Re-throw ApiError before wrapping other errors. The wrapper removes status and retryAfter, so the error handlers cannot use status checks or the server-provided retry delay.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adrapid/client.ts` around lines 54 - 58, Update the ADRAPID_API_BASE
constant to the actual Adrapid API base URL instead of the placeholder. In the
error handling around the client request, re-throw existing ApiError instances
before wrapping other Error values, preserving their status and retryAfter
metadata; retain the unknown-error fallback for non-Error values.

Comment on lines +4 to +17
import type { AdrapidEndpointOutputs } from './types';

export const get: AdrapidEndpoints['exampleGet'] = async (ctx, input) => {
const response = await makeAdrapidRequest<
AdrapidEndpointOutputs['exampleGet']
>(`example/${input.id}`, ctx.key, { method: 'GET' });

await logEventFromContext(
ctx,
'adrapid.example.get',
{ ...input },
'completed',
);
return response;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline packages/adrapid --items all --type function
rg -n -C 4 'makeAdrapidRequest|AdrapidEndpointOutputSchemas|\.parse\(' packages/adrapid

Repository: corsairdev/corsair

Length of output: 4095


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- packages/adrapid/client.ts ---'
cat -n packages/adrapid/client.ts
printf '%s\n' '--- packages/adrapid/endpoints/types.ts ---'
cat -n packages/adrapid/endpoints/types.ts
printf '%s\n' '--- packages/adrapid/endpoints/example.ts ---'
cat -n packages/adrapid/endpoints/example.ts
printf '%s\n' '--- endpoint registration and invocation ---'
rg -n -C 6 'adrapidEndpointSchemas|adrapidEndpointsNested|exampleGet|RequiredPluginEndpointSchemas|output.*parse|parse.*output' packages/adrapid packages/corsair

Repository: corsairdev/corsair

Length of output: 15720


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- endpoint schema consumers ---'
rg -n -C 8 'endpointSchemas|\.output\b|outputSchema|safeParse|parseAsync|safeParseAsync' packages/corsair packages --glob '*.{ts,tsx,js,jsx}' | head -n 500
printf '%s\n' '--- endpoint execution definitions ---'
rg -n -C 10 'execute.*endpoint|invoke.*endpoint|endpoints.*schemas|EndpointTree|CorsairEndpoint' packages/corsair/core packages/corsair --glob '*.{ts,tsx}'

Repository: corsairdev/corsair

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- all endpointSchemas references ---'
rg -l 'endpointSchemas' packages --glob '*.{ts,tsx,js,jsx}' | sort
printf '%s\n' '--- schema parsing references in Corsair core ---'
rg -n 'safeParse|parseAsync|safeParseAsync|\.parse\(' packages/corsair/core packages/corsair/http packages/corsair/client packages/corsair --glob '*.{ts,tsx}' --glob '!tests/**' | head -n 300
printf '%s\n' '--- binding and plugin construction ---'
rg -n -C 12 'bindEndpoints|function bind|const bind|endpoints:' packages/corsair/core packages/corsair --glob '*.{ts,tsx}' --glob '!tests/**' | head -n 500

Repository: corsairdev/corsair

Length of output: 7702


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Corsair core files ---'
git ls-files packages/corsair/core | sed -n '1,240p'
printf '%s\n' '--- endpoint schema references in core ---'
rg -n -C 12 'endpointSchemas' packages/corsair/core
printf '%s\n' '--- endpoint binding references ---'
rg -n -C 10 'bindEndpoints|BoundEndpointFn|enforcePermission|endpointPath' packages/corsair --glob '*.{ts,tsx}' --glob '!tests/**' | head -n 500

Repository: corsairdev/corsair

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

example = Path("packages/adrapid/endpoints/example.ts").read_text()
client = Path("packages/adrapid/client.ts").read_text()
plugins = Path("packages/corsair/core/plugins/index.ts").read_text()
inspect = Path("packages/corsair/core/inspect/index.ts").read_text()
bind = Path("packages/corsair/core/endpoints/bind.ts").read_text()

checks = {
    "endpoint calls typed request helper": "makeAdrapidRequest<" in example,
    "endpoint imports output schema": "AdrapidEndpointOutputSchemas" in example,
    "endpoint parses response": ".parse(" in example or ".safeParse(" in example,
    "helper returns request result directly": "return await request<T>" in client,
    "plugin docs describe schemas as inspection metadata": "Used by get_schema()" in plugins,
    "inspection converts output schema to docs": "zodToFormSchema(schemas.output)" in inspect,
    "binding layer references endpoint schemas": "endpointSchemas" in bind,
}

for name, result in checks.items():
    print(f"{name}: {result}")

assert checks["endpoint calls typed request helper"]
assert not checks["endpoint imports output schema"]
assert not checks["endpoint parses response"]
assert checks["helper returns request result directly"]
assert checks["plugin docs describe schemas as inspection metadata"]
assert checks["inspection converts output schema to docs"]
assert not checks["binding layer references endpoint schemas"]
PY

Repository: corsairdev/corsair

Length of output: 468


Validate the provider response before logging completion.

makeAdrapidRequest<AdrapidEndpointOutputs['exampleGet']> provides only a static type. Parse the response with AdrapidEndpointOutputSchemas.exampleGet so malformed provider data cannot reach consumers or produce a completed event.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adrapid/endpoints/example.ts` around lines 4 - 17, Validate the
response in the get endpoint using AdrapidEndpointOutputSchemas.exampleGet
immediately after makeAdrapidRequest and before logEventFromContext. Use the
parsed result as the returned response, so malformed provider data is rejected
before consumers receive it or the completed event is logged.

Comment thread packages/adrapid/index.ts
Comment on lines +115 to +122
export const adrapidAuthConfig = {
api_key: {
account: ['tenant_external_id'] as const,
},
oauth_2: {
account: ['tenant_external_id'] as const,
},
} as const satisfies PluginAuthConfig;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- plugin files ---'
git ls-files 'packages/adrapid/**' '.github/PLUGIN_PR_RULES.md' 'packages/corsair/core/constants.ts'

printf '%s\n' '--- relevant source ---'
for f in packages/adrapid/index.ts \
         packages/adrapid/webhooks/tenant-matcher.ts \
         packages/adrapid/webhooks/oauth-tenant-link.ts; do
  if [ -f "$f" ]; then
    printf '\n### %s\n' "$f"
    cat -n "$f"
  fi
done

printf '%s\n' '--- adrapid references ---'
rg -n -C 3 'tenant_external_id|tenant-matcher|oauth-tenant-link|adrapid' packages .github \
  -g '!node_modules' -g '!dist' -g '!build' || true

printf '%s\n' '--- plugin rules ---'
if [ -f .github/PLUGIN_PR_RULES.md ]; then
  cat -n .github/PLUGIN_PR_RULES.md
fi

Repository: corsairdev/corsair

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- plugin rules ---'
cat -n .github/PLUGIN_PR_RULES.md

printf '%s\n' '--- adrapid package metadata and docs ---'
for f in packages/adrapid/package.json packages/adrapid/schema/database.ts packages/adrapid/schema/index.ts packages/adrapid/webhooks/types.ts packages/adrapid/schema.test.ts; do
  printf '\n### %s\n' "$f"
  cat -n "$f"
done
find packages/adrapid -maxdepth 2 -type f \( -iname 'README*' -o -iname '*.md' \) -print

printf '%s\n' '--- comparable tenant implementations ---'
for f in packages/{slack,gitlab,github,hubspot,linear}/webhooks/tenant-matcher.ts \
         packages/{slack,gitlab,github,hubspot,linear}/webhooks/oauth-tenant-link.ts; do
  if [ -f "$f" ]; then
    printf '\n### %s\n' "$f"
    cat -n "$f"
  fi
done

printf '%s\n' '--- core tenant-link call sites and types ---'
rg -n -C 5 'oauthWebhookTenantLinkResolver|pluginTenantWebhookMatcher|WebhookTenantMatch|authConfig\.account|tenant_external_id' packages/corsair packages -g '*.ts' -g '*.tsx' \
  | head -n 1200

Repository: corsairdev/corsair

Length of output: 50375


🌐 Web query:

Adrapid API OAuth webhook tenant identifier tenant_external_id webhook payload

💡 Result:

The Adrapid platform provides a REST API primarily authenticated via bearer tokens [1][2] and supports OAuth-based integration through third-party platforms [3]. While Adrapid documentation outlines tools for managing banners, templates, and user access [2][4], there is no public technical documentation confirming a standardized internal API schema that utilizes a specific "tenant_external_id" field within a native webhook payload. For integrations involving Adrapid, tenant management and webhook handling are typically managed through the following mechanisms: 1. Integration Layer: Platforms that integrate with Adrapid (such as automation tools or middleware) often abstract the webhook handling [3][5]. In these environments, identifiers like tenant IDs are managed by the integration provider's own infrastructure rather than natively as a required field in the Adrapid API request payload [5]. 2. Administrative API: Access and user-specific configurations are handled via the Adrapid administrative API, which includes endpoints for retrieving user-specific access URLs and widget configurations [4]. 3. Webhook Handling: If you are implementing custom webhooks to receive notifications from Adrapid, you are responsible for defining the endpoint. Since Adrapid documentation does not explicitly document a "tenant_external_id" as a mandatory part of its outgoing webhook payload, you should inspect the raw JSON payload sent to your defined endpoint to determine which identifiers (such as user ID or account ID) are available for your mapping needs. If you are developing a multi-tenant application, it is standard practice to map the Adrapid User ID or an internal account identifier to your own "tenant_external_id" within your database upon receiving a webhook, rather than expecting that specific field to be natively present in the payload. [1][4][5]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- OAuth link persistence path ---'
sed -n '1,180p' packages/corsair/webhooks/resolve-oauth-tenant-link.ts
sed -n '1,280p' packages/corsair/webhooks/tenant-links.ts

printf '%s\n' '--- exact tenant-link type and auth configuration definitions ---'
rg -n -A 18 -B 8 'export type WebhookTenantMatch|type WebhookTenantMatch|export type PluginAuthConfig|interface PluginAuthConfig|account\??:' packages/corsair/core packages/corsair -g '*.ts' | head -n 500

printf '%s\n' '--- adrapid TODO and placeholder inventory ---'
rg -n 'TODO|api\.example\.com|tenant_external_id|example' packages/adrapid

printf '%s\n' '--- tracked test inventory for adrapid ---'
git ls-files 'packages/adrapid/**/*test.ts' 'packages/adrapid/**/*.test.ts' | sort

printf '%s\n' '--- bounded behavioral probe of the shown extractors ---'
python3 - <<'PY'
from typing import Any

def first_string(values):
    return next((v for v in values if isinstance(v, str) and v), None)

def as_record(value):
    return value if isinstance(value, dict) else None

def matcher(body: Any):
    if not isinstance(body, dict):
        return None
    external_id = first_string([
        body.get("tenant_external_id"),
        (as_record(body.get("data")) or {}).get("tenant_external_id"),
    ])
    return None if not external_id else {
        "linkType": "tenant_external_id",
        "externalId": external_id,
    }

def resolver(tokens: dict):
    value = tokens.get("tenant_external_id")
    external_id = value if isinstance(value, (str, int, float)) and not isinstance(value, bool) and str(value) else None
    if external_id is not None:
        return {"linkType": "tenant_external_id", "externalId": str(external_id)}
    if not tokens.get("access_token"):
        return None
    return None

cases = [
    ("documented example payload", {"type": "example", "created_at": "x", "data": {"id": "acct-1"}}),
    ("placeholder top-level field", {"tenant_external_id": "acct-1"}),
    ("placeholder nested field", {"data": {"tenant_external_id": "acct-1"}}),
]
for name, body in cases:
    print(name, "=>", matcher(body))

for name, tokens in [
    ("token has no placeholder id", {"access_token": "token"}),
    ("token has placeholder id", {"access_token": "token", "tenant_external_id": "acct-1"}),
    ("token has no access token", {}),
]:
    print(name, "=>", resolver(tokens))
PY

Repository: corsairdev/corsair

Length of output: 49034


Implement provider-specific tenant linkage before enabling tenant routing.

adrapidAuthConfig registers the placeholder tenant_external_id. The matcher only reads that placeholder, while the webhook schema defines data.id. When OAuth tokens omit tenant_external_id, the resolver returns null, so OAuth does not store a webhook link. Determine the provider’s stable account identifier, then use the same link type in adrapidAuthConfig, the webhook matcher, and the OAuth resolver. Remove the placeholder URL and TODO stubs.

📍 Affects 3 files
  • packages/adrapid/index.ts#L115-L122 (this comment)
  • packages/adrapid/webhooks/tenant-matcher.ts#L17-L24
  • packages/adrapid/webhooks/oauth-tenant-link.ts#L9-L30
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adrapid/index.ts` around lines 115 - 122, Determine AdRapid’s stable
account identifier and use that single link type consistently: update
adrapidAuthConfig in packages/adrapid/index.ts (115-122), the webhook matcher in
packages/adrapid/webhooks/tenant-matcher.ts (17-24), and the OAuth resolver in
packages/adrapid/webhooks/oauth-tenant-link.ts (9-30). Ensure OAuth tokens
resolve and store the webhook link using the provider’s webhook
data.id-compatible identifier, and remove the placeholder URL and TODO stubs.

Comment on lines +56 to +62
export function verifyAdrapidWebhookSignature(
request: WebhookRequest<AdrapidWebhookPayload>,
secret: string,
): { valid: boolean; error?: string } {
// TODO: Implement webhook signature verification
return { valid: true };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Corsair webhook request types and existing signature-verification patterns.
rg -n -C 5 --glob '*.ts' \
  'verify[A-Za-z]*WebhookSignature|createHmac|timingSafeEqual|RawWebhookRequest|WebhookRequest|pluginWebhookMatcher' \
  packages

Repository: corsairdev/corsair

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Adrapid files ---'
git ls-files packages/adrapid

printf '%s\n' '--- Adrapid webhook implementation and callers ---'
rg -n -C 12 \
  'verifyAdrapidWebhookSignature|AdrapidWebhookPayload|event log|logEventFromContext|webhooks' \
  packages/adrapid

printf '%s\n' '--- Core webhook request types and processing ---'
rg -n -C 12 \
  'export (type|interface) (RawWebhookRequest|WebhookRequest)|type RawWebhookRequest|interface RawWebhookRequest|function processWebhook|async function processWebhook' \
  packages/corsair packages/adrapid

Repository: corsairdev/corsair

Length of output: 16148


🌐 Web query:

Adrapid webhook signature verification documentation header HMAC raw body

💡 Result:

There is no publicly available documentation from Adrapid regarding a native webhook signature verification mechanism or specific HMAC header requirements [1][2][3]. Adrapid's current API documentation primarily focuses on REST API endpoints for banner creation and account management, using Bearer token authentication [1][2]. While integrations with third-party automation platforms (such as Zapier) exist, these typically manage authentication and event delivery through the platform's own infrastructure [4]. If you are implementing a custom webhook receiver for Adrapid (or a service calling it), you should follow standard industry practices for HMAC-SHA256 signature verification [5][6][7]: 1. Capture the Raw Body: You must read the incoming request body as raw bytes before any JSON parsing occurs [8][9][7]. If your web framework (e.g., Express) parses the body automatically, re-serializing it will change the byte structure, causing verification to fail [9][6]. 2. Identify the Signature Header: Since no official Adrapid documentation specifies a signature header, you would need to determine if one is provided by the specific integration source or platform you are using [5][6]. Common header names include X-Hub-Signature-256 or X-Webhook-Signature [5][6]. 3. Compute the Expected HMAC: Use the shared secret key to compute an HMAC-SHA256 hash over the captured raw request body [8][9]. 4. Perform Constant-Time Comparison: Compare the computed hash with the hash received in the header using a timing-safe comparison function (e.g., crypto.timingSafeEqual in Node.js) to prevent timing attacks [8][5][7]. If you require webhooks from Adrapid, it is recommended to check the specific integration settings or dashboard of the tool you are using to facilitate the webhook, as that platform may define its own verification protocol [10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Adrapid webhook types ---'
cat -n packages/adrapid/webhooks/types.ts

printf '%s\n' '--- Adrapid plugin configuration and key resolution ---'
sed -n '1,220p' packages/adrapid/index.ts
rg -n -C 10 'webhookSecret|ctx\.key|get_integration_credentials|keyBuilder|webhookHooks' \
  packages/adrapid packages/corsair

printf '%s\n' '--- Webhook request construction and handler invocation ---'
sed -n '176,330p' packages/corsair/webhooks/index.ts
rg -n -C 15 'rawBody|WebhookRequest<' packages/corsair/webhooks packages/corsair/core

printf '%s\n' '--- Relevant existing verifier implementations ---'
sed -n '1,120p' packages/corsair/async-core/webhook-utils.ts
sed -n '240,290p' packages/instagram/webhooks/types.ts
fd -i 'types.ts' packages/github/webhooks packages/xquik/webhooks packages/stripe/webhooks 2>/dev/null \
  | xargs -r rg -n -C 12 'verify.*Signature|rawBody|signature'

Repository: corsairdev/corsair

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

paths = [
    Path("packages/adrapid/webhooks/types.ts"),
    Path("packages/adrapid/webhooks/example.ts"),
    Path("packages/adrapid/index.ts"),
    Path("packages/corsair/core/webhooks/index.ts"),
    Path("packages/corsair/webhooks/index.ts"),
]

for path in paths:
    text = path.read_text()
    print(f"{path}:")
    for term in ("TODO: Implement webhook signature verification",
                 "return { valid: true }",
                 "verifyAdrapidWebhookSignature(request, ctx.key)",
                 "rawBody?: string",
                 "webhookSecret"):
        print(f"  {term!r}: {text.count(term)}")
PY

Repository: corsairdev/corsair

Length of output: 1254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Webhook processing: request creation and handler call ---'
sed -n '176,285p' packages/corsair/webhooks/index.ts
rg -n -C 10 'rawBody:|rawBody\?|handler\\(' packages/corsair | head -n 240

printf '%s\n' '--- Core webhook utilities ---'
cat -n packages/corsair/async-core/webhook-utils.ts | sed -n '1,115p'

printf '%s\n' '--- Existing provider verifier implementations ---'
rg -l 'verify[A-Za-z]+WebhookSignature' packages --glob '*.ts' \
  | while read -r file; do
      echo "--- $file"
      rg -n -C 18 'verify[A-Za-z]+WebhookSignature' "$file"
    done

Repository: corsairdev/corsair

Length of output: 3511


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

path = Path("packages/corsair/webhooks/index.ts")
text = path.read_text()

for pattern in [
    r"const rawRequest = \{.*?\} satisfies RawWebhookRequest;",
    r"const request: WebhookRequest.*?;",
    r"handler\s*:\s*",
    r"rawBody",
    r"keyBuilder",
]:
    print(f"PATTERN {pattern}")
    matches = list(re.finditer(pattern, text, re.S))
    for match in matches:
        start = max(0, match.start() - 500)
        end = min(len(text), match.end() + 900)
        print(text[start:end])
        print("---")
PY

Repository: corsairdev/corsair

Length of output: 4687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

types = Path("packages/adrapid/webhooks/types.ts").read_text()
example = Path("packages/adrapid/webhooks/example.ts").read_text()
plugin = Path("packages/adrapid/index.ts").read_text()
core = Path("packages/corsair/webhooks/index.ts").read_text()

function = re.search(
    r"export function verifyAdrapidWebhookSignature\([\s\S]*?\n\}",
    types,
)
assert function, "Adrapid verifier not found"
body = function.group(0)

assert "request" not in body.split("):", 1)[-1].split("{", 1)[-1], \
    "Verifier body unexpectedly uses request"
assert "secret" not in body.split("):", 1)[-1].split("{", 1)[-1], \
    "Verifier body unexpectedly uses secret"
assert "return { valid: true }" in body

assert "x-adrapid-signature" in plugin
assert "verifyAdrapidWebhookSignature(request, ctx.key)" in example
assert "if (!verification.valid)" in example
assert "logEventFromContext" in example
assert "rawBody: typeof body === 'string' ? body : JSON.stringify(body)" in core

print("A request with any x-adrapid-signature header reaches the handler.")
print("The current verifier returns valid=true without inspecting request or secret.")
print("The handler logs the payload after the verifier returns valid=true.")
print("The webhook pipeline preserves the input string as rawBody when callers provide it.")
PY

Repository: corsairdev/corsair

Length of output: 453


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Adrapid webhook tests and signature-related documentation ---'
rg -n -C 8 \
  'Adrapid|x-adrapid-signature|webhookSecret|signature' \
  packages/adrapid README.md docs .github 2>/dev/null \
  | head -n 240

Repository: corsairdev/corsair

Length of output: 13294


Implement Adrapid webhook signature verification before accepting events.

verifyAdrapidWebhookSignature always returns { valid: true }. Since the matcher only checks for x-adrapid-signature, an unauthenticated request can reach logEventFromContext. Use the configured signature scheme with request.rawBody, reject missing or malformed signatures and secrets, and use a timing-safe comparison for HMAC signatures. Add tests for valid, invalid, missing, and malformed signatures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adrapid/webhooks/types.ts` around lines 56 - 62, The
verifyAdrapidWebhookSignature function must perform real authentication instead
of always returning valid. Implement the configured signature scheme using
request.rawBody and the provided secret, reject missing or malformed signatures
or secrets, and compare HMAC signatures with a timing-safe method; add tests
covering valid, invalid, missing, and malformed signatures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app App / Hub-facing app code bot:round-1 Review bot posted consolidated findings core Changes in packages/corsair gate:failed Plugin PR gate checks failing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant