Skip to content

[comp] Production Deploy#2209

Merged
tofikwest merged 13 commits intoreleasefrom
main
Mar 5, 2026
Merged

[comp] Production Deploy#2209
tofikwest merged 13 commits intoreleasefrom
main

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 3, 2026

This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.

github-actions bot and others added 2 commits March 2, 2026 20:26
…ner (#2204)

* feat(app): show confirmation for submission deletion

* feat(api): create a delete endpoint to remove a submission

* feat(app): integrate delete-submission endpoint on documents page

* fix(app): add delete submission with findings cache invalidation

* fix(api): make delete-submission api endpoint accessible by admin/owner

* fix(app): make delete-submission button visible only for admin/owner

* fix(app): keep submission-delete dialog open until delete completes and show laoding state

---------

Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com>
@vercel
Copy link

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment Mar 5, 2026 2:29pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped Mar 5, 2026 2:29pm

Request Review

@cursor
Copy link

cursor bot commented Mar 3, 2026

PR Summary

High Risk
High risk because it introduces new Stripe billing flows (Checkout, Billing Portal, webhooks, immediate overage charging) plus new DB tables/migrations and changes to provider/webhook payload contracts and deletion APIs.

Overview
Penetration tests now use Stripe subscription billing instead of a mock checkout flow. This adds org billing/subscription persistence (OrganizationBilling, PentestSubscription), a new Billing settings page, a Stripe webhook endpoint (/api/webhooks/stripe-pentest), and client-side run creation that charges overages immediately and optimistically updates SWR caches.

Penetration test provider integration was tightened and expanded. Maced client schemas were updated (nullable normalization, new fields like notificationEmail, progress shape changes, webhook payload expects runId), the API now exposes GET /v1/security-penetration-tests/github/repos and auto-fetches an org GitHub token via the integration platform to list/select repos in the UI.

New operational tooling and workflows. Adds a scheduled/PR GitHub Action canary (test:e2e:maced) to detect Maced contract drift, introduces bulk automation-failure digest notifications (new internal endpoint + email template + notifier logic), and adds evidence submission deletion (new DELETE API plus admin UI actions/confirmation dialog).

Includes assorted UI cleanups (security results “all passed” banner, sidebar icon tweaks, settings billing tab, context table pagination, only isActive members in people lists, remove draggable framework cards, minor layout fixes).

Written by Cursor Bugbot for commit caef0d9. This will update automatically on new commits. Configure here.

…ntract fixes (#2208)

* feat(security): align penetration test lifecycle with Maced contract

* fix(security): remove empty-string defaults from Maced run parser

* test(api): add Maced contract canary e2e and CI workflow

* fix(security): make Maced run parser transition-safe for nullish runtime IDs

* fix(security): align Maced client and types to canonical OpenAPI contract

- Drop fields absent from spec: sandboxId, workflowId, sessionId,
  failedReason (from Maced), userId, organizationId, phase, agent,
  completedAgentNames from all schemas, interfaces and test fixtures
- Add notificationEmail to PentestRun schema, CreatePentestRequest
  payload, app-side PentestRun interface and PentestCreateRequest
- Move webhookToken to a dedicated CreatePentestRun schema (POST-only)
- Progress cell now renders "In progress (x/y)" without phase prefix
- Update canary assertions and all unit test mocks to match new shapes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): skip prisma generate in maced contract canary workflow

The shared bun-install action runs `bun prisma generate` which fails at
the repo root since the Prisma schema lives in a sub-package. The canary
only needs the HTTP client and has no Prisma dependency, so inline the
install without the generate step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
'provisioning',
'cloning',
'running',
];
Copy link

Choose a reason for hiding this comment

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

Unused dead code constant introduced

Low Severity

inProgressStatus is declared but never referenced anywhere in the file or codebase. The same set of statuses is already covered by isReportInProgress from ../lib.ts, which is already imported and used throughout this file.

Fix in Cursor Fix in Web

* feat: add bulk automation failure notification endpoint and email template

Add POST /v1/internal/tasks/notify-bulk-automation-failures endpoint with
FailedTaskDto and NotifyBulkAutomationFailuresDto DTOs, add
notifyBulkAutomationFailures service method (recipients: task assignees +
org admins/owners), and add consolidated email template that lists failed
tasks with (X/Y failed) counts capped at 15 items.

* fix: remove unused title and assigneeId fields from bulk failures DB query
Add overflow-hidden to the card container, min-w-0 to the flex child,
shrink-0 to the icon, and break-words to the reason text to prevent
long text from pushing the UI off-screen.
@vercel vercel bot temporarily deployed to staging – portal March 3, 2026 19:48 Inactive
Added fromYear/toYear props to the Calendar component so users can
jump directly to a year via dropdown instead of clicking month by month
all the way back to e.g. 2009.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel vercel bot temporarily deployed to staging – portal March 3, 2026 19:50 Inactive
…2213)

Added a note on cloud integration cards and in the connect dialog
making it clear that these integrations are only used for Cloud
Security Tests and credentials are used for read-only scans.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel vercel bot temporarily deployed to staging – portal March 3, 2026 19:51 Inactive
The context table was loading all entries without pagination, which
doesn't scale. The backend already supported pagination via search
params but the frontend wasn't using it.

Now uses URL-based pagination with the Table component's built-in
pagination prop, matching the pattern used by VendorsTable.
@vercel vercel bot temporarily deployed to staging – portal March 3, 2026 19:52 Inactive
When a cloud security scan completes and all checks pass with no
issues, the page now shows a green "All checks passed" banner instead
of just a table of green rows. Makes it immediately obvious to
customers and auditors that everything is clean.
...(report as SecurityPenetrationTest),
failedReason,
};
}
Copy link

Choose a reason for hiding this comment

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

Webhook token leaks to client via object spread

High Severity

mapMacedRunToSecurityPenetrationTest uses ...(report as SecurityPenetrationTest) which spreads all runtime properties of the input object. When called from createReport with a MacedCreatePentestRun (which includes the sensitive webhookToken field), the token leaks into the serialized API response even though it was intentionally removed from the SecurityPenetrationTest interface. The type cast only hides the field from TypeScript — it does not strip it at runtime.

Additional Locations (1)

Fix in Cursor Fix in Web

* feat(pentest): subscription-based billing model

Replaces the mock checkout redirect with an inline subscription billing
model. Organizations subscribe to a monthly plan (3 runs included) and
overage runs are charged immediately via Stripe PaymentIntent at run
creation time — no redirect required.

- Add PentestSubscription DB model (organization relation, period tracking)
- Add billing server actions: subscribeToPentestPlan, handleSubscriptionSuccess,
  checkAndChargePentestBilling (blocks run creation on billing failure)
- Add /[orgId]/security/penetration-tests/subscription management page
- Add /api/webhooks/stripe-pentest webhook handler (subscription updated/deleted)
- Remove mockCheckout from API DTO and client types
- Update useCreatePenetrationTest to call billing check before API post
- Update page client: remove checkout redirect/search-param handling,
  navigate directly to report detail on success
- Add STRIPE_PENTEST_SUBSCRIPTION_PRICE_ID, STRIPE_PENTEST_OVERAGE_PRICE_ID,
  STRIPE_PENTEST_WEBHOOK_SECRET env vars
- Delete mock checkout page
- Update all tests to reflect new flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(billing): use SubscriptionItem for period dates in Stripe SDK v20+

In stripe@20.x with API version 2025-12-15.clover, current_period_start
and current_period_end were moved from the root Subscription type to
SubscriptionItem. Read them via subscription.items.data[0] instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(security): add Pentest Billing tab and Stripe billing portal

- Add "Pentest Billing" as a second tab in SecuritySidebar, pointing to
  the existing subscription management page
- Fix active-tab detection so Penetration Tests doesn't stay highlighted
  when on the Billing sub-page
- Add createBillingPortalSession server action so active subscribers can
  manage their payment method via Stripe's hosted portal
- Fix return URL to use NEXT_PUBLIC_BETTER_AUTH_URL (was using
  NEXT_PUBLIC_APP_URL which isn't defined)
- Fix subscribe button copy to reflect actual price ($99/month)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(pentest): address PR review findings

P1 — billing auth: validate session and org membership in
checkAndChargePentestBilling before any DB/Stripe calls, so a caller
cannot trigger billing against an org they don't belong to.

P1 — billing order: move checkAndChargePentestBilling to after successful
run creation so a transient provider failure never charges the customer
without delivering a run. Threshold adjusted from `<` to `<=` so the
included-run count is still respected once the new run is counted in DB.

P2 — GitHub token key: integration-platform GitHub connections use OAuth2
and store the token under `access_token`; fix getGithubTokenForOrg to
read that field instead of the legacy PAT key `GITHUB_TOKEN`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(billing): lift stripeCustomerId to OrganizationBilling + Settings > Billing hub

- Add OrganizationBilling model (one per org, owns stripeCustomerId) so
  future subscription products share a single Stripe customer
- PentestSubscription now relates to OrganizationBilling via FK instead
  of owning stripeCustomerId directly
- New Settings > Billing page as the single hub for all app subscriptions
- Remove Pentest Billing tab from Security sidebar
- Delete old /security/penetration-tests/subscription page
- Update "Manage subscription" link to /settings/billing
- Add requireOrgMember() guard to all four billing server actions
  (subscribeToPentestPlan, handleSubscriptionSuccess, createBillingPortalSession
  were previously missing the cross-tenant auth check)
- Validate Stripe session ownership in handleSubscriptionSuccess:
  reject if session customer doesn't match existing org billing record

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(webhook): handle checkout.session.completed to activate subscription server-side

Previously, PentestSubscription was only created when the user returned to
the /settings/billing success URL. If they closed the tab or the browser
crashed after Stripe Checkout, the subscription would never activate.

The webhook is now the primary activation path:
- checkout.session.completed → look up OrganizationBilling by stripeCustomerId,
  retrieve full subscription, upsert PentestSubscription
- handleSubscriptionSuccess on the return URL becomes an idempotent fallback
  (safe to call again since both paths use upsert)

Note: checkout.session.completed must be added to the Stripe webhook's
event subscriptions in the dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(billing): harden Stripe customer binding and URL construction

- Remove findStripeCustomerByDomain fallback in subscribeToPentestPlan:
  organization.website is tenant-controlled so domain-based customer
  reuse could let a malicious org bind to another company's Stripe
  customer. Always create a fresh customer when no billing row exists.

- Strengthen handleSubscriptionSuccess session ownership check:
  previously only rejected mismatched customers when an OrganizationBilling
  row already existed. Now if no row exists (edge case — subscribeToPentestPlan
  always creates one first), verify the Stripe customer's metadata.organizationId
  matches before accepting the session.

- Fix Stripe return URLs to always be absolute: derive origin from
  NEXT_PUBLIC_BETTER_AUTH_URL with a request-header fallback so Stripe
  never receives a relative URL when the env var is unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(billing): deduplicate concurrent overage charges + preserve manual repo URL input

- Add Stripe idempotency key to paymentIntents.create scoped to
  orgId + period start + run number; concurrent creates at the quota
  boundary now deduplicate to a single charge instead of double-billing

- Show manual URL input alongside the GitHub repo selector so users
  with more than 100 repos (beyond the first-page fetch) can still
  paste a repo URL directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(billing): use per-run ID for overage charge idempotency key

The previous key used `runsThisPeriod` (an aggregate count), meaning
two concurrent creates could observe the same count and share the same
key — Stripe would deduplicate to a single PaymentIntent even when two
separate overage runs were billed.

Replace with `pentest-overage-{orgId}-{runId}` so each run gets a
globally unique idempotency key, eliminating the underbilling race.

Also include Codex-added tests for the GitHub connection UI flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
'provisioning',
'cloning',
'running',
];
Copy link

Choose a reason for hiding this comment

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

Delete endpoint description claims auditor role is allowed

Low Severity

The inProgressStatus array is defined but never referenced anywhere in the file. The code uses the isReportInProgress function imported from ../lib instead, making this constant dead code.

Additional Locations (1)

Fix in Cursor Fix in Web

…buse (#2217)

- Remove returnUrl/returnBaseUrl parameters from subscribeToPentestPlan
  and createBillingPortalSession; URLs are now computed server-side from
  env vars + request host, preventing open-redirect phishing via direct
  server action invocation
- Validate runId in checkAndChargePentestBilling against the DB to ensure
  the run exists and belongs to the org, preventing repeated overage
  charges via arbitrary fabricated runId strings
- Remove duplicate PageLayout/PageHeader from billing page (layout
  already provides them for all settings routes)

Co-authored-by: Claudio Fuentes <claudio@trycomp.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

} else {
checkoutUrl = response.data?.checkoutUrl ?? fallbackCheckoutUrl;
}
await checkAndChargePentestBilling(organizationId, reportId);
Copy link

Choose a reason for hiding this comment

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

Billing enforcement only client-side, backend has none

High Severity

checkAndChargePentestBilling is only called from the client-side hook after the backend API has already created the run at the provider. The NestJS createReport service method performs no billing or subscription check whatsoever. Any authenticated org member can call the POST /v1/security-penetration-tests API directly (bypassing the frontend) to create unlimited pentest runs without being billed or subscription-gated.

Additional Locations (1)

Fix in Cursor Fix in Web

#2218)

* refactor(ui): improve nav icons + remove overview drag-and-drop

Icons (AppShellWrapper — rail):
- Compliance: CertificateCheck → Badge (certification badge)
- Trust:      CloudAuditing   → Globe (public/global trust center)
- Security:   Security        → ManageProtection (protection management)

Icons (AppSidebar — compliance sidebar):
- Auditor View:  TaskComplete → DocumentSigned  (auditors sign off)
- Controls:      Security     → SettingsAdjust  (adjustable params, no dupe)
- Documents:     Catalog      → FolderDetails   (folder of docs)
- People:        Group        → UserMultiple     (standard multi-user)
- Risks:         Warning      → Scale           (weighing risk)
- Vendors:       ShoppingBag  → Partnership     (business partners)
- Questionnaire: Document     → DocumentTasks   (task-filled form)
- Integrations:  Integration  → Connect         (connecting systems)
- Cloud Tests:   Chemistry    → TestTool        (test tooling)

Overview:
- Replace <DraggableCards> with a plain 2-column grid
- Delete DraggableCards.tsx, SortableCard.tsx, useCardOrder.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* revert(ui): restore original compliance sidebar icons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(billing): look up run by providerRunId not internal id

The id returned by the create API is the provider run ID stored
in providerRunId, not the internal ptr... primary key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com>
@vercel vercel bot temporarily deployed to staging – portal March 5, 2026 14:27 Inactive
@tofikwest tofikwest merged commit a686844 into release Mar 5, 2026
13 of 16 checks passed
@claudfuen
Copy link
Contributor

🎉 This PR is included in version 1.88.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants