Skip to content

[comp] Production Deploy#3316

Merged
tofikwest merged 13 commits into
releasefrom
main
Jul 1, 2026
Merged

[comp] Production Deploy#3316
tofikwest merged 13 commits into
releasefrom
main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

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.


Summary by cubic

Fixes Trust Centre ISO badge mapping and prevents offboarded users from falling into the onboarding loop while keeping invite flows intact. Subprocessor pages show correct badges (e.g., Scaleway ISO 27001 + GDPR), and offboarded users see an access-removed page instead (CS-569).

  • Bug Fixes
    • Trust Centre: tighten ISO matching to require an ISO (optional IEC) prefix and bounded numbers for 27001, 42001, 9001, allowing an optional year but not longer numbers; centralize the matcher; add tests for positives and false positives (e.g., Scaleway, ISO 9001:2015, ISO/IEC 42001, 27017/27018, "Catalog 19001", "ISO 90010").
    • Auth (CS-569): /v1/auth/me now includes hasInactiveMembership; root page and /setup use a shared resolver (invite > access-removed > onboarding) and honor ?inviteCode=; add /auth/access-removed page and a backstop in create-organization-minimal to block spurious org creation; add route/page/controller tests.

Written for commit 67e6730. Summary will update on new commits.

Review in cubic

github-actions Bot and others added 3 commits July 1, 2026 04:15
…isk task

## Problem
The Trust Centre subprocessor page shows incomplete compliance badges for Scaleway, displaying only GDPR while missing ISO/IEC 27001 certification that is verified in the Vendors tab. This misleads auditors and prospective customers about the vendor's security posture.

## Root cause
The certification-to-badge mapping in trust-portal.service.ts normalizes cert names by stripping non-alphanumeric chars, turning "ISO/IEC 27001:2022" into "isoiec270012022". The check then looks for 'iso27001' or 'iso 27001' (the latter impossible post-normalization), so the cert is not recognized and gets dropped. The parallel code path in vendor-risk-assessment-task.ts was hardened in April to handle this (bare '27001' substring check), but trust-portal was left behind, creating an asymmetry.

## Fix
Update the mapCertificationToBadgeType logic in trust-portal.service.ts to include a '27001' substring check, matching the vendor-risk-assessment-task implementation. This recognizes the normalized cert string and maps it correctly to the ISO 27001 badge type.

## Explicitly NOT touched
Data in the Vendors tab (Capawesome) remains unchanged. The fix only corrects the mapping logic to properly recognize existing cert data. HDS badge handling is out of scope for this PR.

## Verification
✅ Scaleway vendor card now displays ISO 27001 badge alongside GDPR on Trust Centre Subprocessors page
✅ Badge set matches verified certifications from Vendors tab
✅ No regression on other vendor mappings
…-subprocessor-badge

fix(trust-portal): sync iso 27001 certification mapping with vendor-risk task
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment Jul 1, 2026 3:23pm
comp-framework-editor (staging) Ready Ready Preview, Comment Jul 1, 2026 3:23pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped Jul 1, 2026 3:23pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 2 files

Confidence score: 4/5

  • In apps/api/src/trust-portal/trust-portal.service.ts, using normalized.includes('9001') can misclassify non-ISO entries (for example IDs containing 19001) as ISO 9001, which could surface incorrect trust/certification status to users; tighten the match to explicit ISO patterns (e.g., iso9001 or a bounded regex) before merging.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/api/src/trust-portal/trust-portal.service.ts Outdated
tofikwest and others added 7 commits July 1, 2026 10:24
When a user is removed/offboarded from their only organization, their sole
membership is soft-deleted (deactivated=true, isActive=false). GET /v1/auth/me
filters those out, so the app saw the user as having zero organizations and
silently redirected them to /setup — the onboarding flow. Completing it created
a brand-new empty org and pointed the session at it, so every subsequent login
re-entered onboarding: an offboarding loop that produced throwaway orgs and an
"Unauthorized" screen on the real org.

The three code paths that read membership disagreed on which memberships
"count", so nothing distinguished a genuinely new user from an offboarded one.

Fix (targeted, additive):
- /v1/auth/me now returns hasInactiveMembership so the app can tell an
  offboarded user (0 active, >=1 deactivated) apart from a brand-new user
  (no memberships at all).
- The landing page and the /setup entry route an offboarded user to a new
  /auth/access-removed interstitial instead of onboarding. New users and users
  adding an additional org are unaffected.
- createOrganizationMinimal refuses to create an org for an offboarded user as
  a DB-level backstop (immune to a transient /v1/auth/me failure).

The access-removed page's primary action is Sign out, since the common trigger
is a domain change where the old account keeps signing in after being removed.

Tests: page-level routing guard (new user vs offboarded vs pending invite) and
the /v1/auth/me hasInactiveMembership computation.

Refs: CS-569

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6zcF9bvPP1UwZgzK21cw1
… prefix

## Problem
mapCertificationToBadgeType matched ISO standards with bare-number substring
checks (`includes('27001')`, `includes('42001')`, `includes('9001')`). Any
certification whose type merely contained those digits — e.g. a catalog id
"19001" or a vendor-specific naming scheme — was misclassified as the
corresponding ISO badge, surfacing a certification the vendor never held on the
public Trust Centre. Cubic flagged the `9001` case (PR #3315); `27001` and
`42001` shared the identical bug class.

## Fix
Match by number but require an "iso" prefix via a bounded regex
`/iso(?:iec)?<number>/`. The optional "iec" preserves the original PR #3315 fix
for joint ISO/IEC standards, whose "IEC" infix breaks a naive
`includes('iso27001')` check ("ISO/IEC 27001:2022" normalizes to
"isoiec270012022"). The digits alone are no longer enough to match.

## Tests
- ISO 9001:2015 -> iso9001 (positive)
- ISO/IEC 42001:2023 -> iso42001 (iec-infix parity, previously untested)
- "Catalog 19001" does NOT map to iso9001, while a real GDPR cert still maps
  (the flagged false-positive regression)
- Existing ISO/IEC 27001:2022 regression still passes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZotUzpY9RZt9JDsEJyrWS
…t /setup

cubic review (CS-569): the /setup entry route ran the offboarded-user
redirect before invite handling, so an offboarded user with a pending
invite entering via /setup?inviteCode=... (handled downstream by
/setup/[setupId]) was bounced to /auth/access-removed before the invite
could be accepted — a dead-end for the legitimate re-invite flow.

Mirror the root landing page's precedence: skip the guard when an
?inviteCode= is present (handled downstream), and for an offboarded user
with a pending invitation redirect to /invite/{id} instead of the
access-removed dead-end. New/active users are unaffected.

Adds route tests: offboarded+no-invite -> access-removed;
offboarded+?inviteCode -> passes through; offboarded+pendingInvitation ->
/invite/{id}; new user -> onboarding.

Refs: CS-569

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6zcF9bvPP1UwZgzK21cw1
…ger one

## Problem (cubic review, PR #3318)
The ISO badge regexes matched the standard number as a bare substring, so an
"iso"-prefixed value with extra trailing digits — e.g. "ISO 90010" (normalizes
to "iso90010") — still matched "iso9001" and earned the ISO 9001 badge. The same
held for 27001/42001 (e.g. ISO/IEC 27017 shares the "2701" prefix with 27001).

## Fix
Append "(?:\d{4})?(?!\d)" to each ISO regex: allow an optional 4-digit year
(the only digits that legitimately follow a standard number, e.g. ":2015"/
":2022") but forbid any other trailing digit. This keeps year suffixes working
while rejecting longer numbers that merely start with the standard number.

## Tests
- "ISO 90010" does NOT map to iso9001 (cubic's case), GDPR on the same vendor
  still maps
- "ISO/IEC 27017:2015" does NOT map to iso27001 (real distinct standard)
- All prior positives still pass: ISO 9001:2015, ISO/IEC 42001:2023,
  ISO/IEC 27001:2022 -> 6/6 in the suite

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZotUzpY9RZt9JDsEJyrWS
The offboard-guard decision was hand-written in two places (app/page.tsx
and the /setup route), which risked exactly the kind of drift between
paths that caused CS-569. Extract it into a single shared helper,
resolveNoActiveOrgRedirect (invite > offboarded > new user), and use it
from both call sites so they can't diverge.

Also harden the /setup invite short-circuit: read the ?inviteCode= value
and treat an empty one as "no invite" (matching the downstream truthy
check in /setup/[setupId]) instead of only checking key presence.

Behavior is unchanged; adds a unit test for the shared decision.

Refs: CS-569

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6zcF9bvPP1UwZgzK21cw1
…elper

Extract the three ISO badge checks into a single matchesIsoStandard(normalized,
number) helper so the boundary rule lives in exactly one place and can't drift
between the 27001/42001/9001 checks (the last cubic finding was a boundary that
existed on one line but not conceptually shared). Behaviour is byte-identical to
the previous inline regexes.

Verified safe against the real data:
- Certification `type` values are AI-extracted names (see the extraction schema:
  "SOC 2 Type II, ISO 27001, ISO 42001, ISO 27017, ISO 27018, ..."), which always
  carry the "ISO" prefix, so requiring the prefix drops no legitimate badge.
- getAllVendorsWithSync re-derives and overwrites complianceBadges on read, so
  this matcher governs the displayed set and self-heals.
- The emitted badge type strings are unchanged (soc2/iso27001/iso42001/gdpr/
  hipaa/pci_dss/nen7510/iso9001), matching the frontend BADGE_ICONS/BADGE_LABELS
  keys in TrustPortalVendors.tsx, so rendering is unaffected.

Tests (8 total) now cover the real cert-name matrix: positives (ISO 9001:2015,
ISO/IEC 27001:2022, ISO/IEC 42001:2023, a full realistic mix) and negatives
(ISO 90010, ISO/IEC 27017, ISO/IEC 27018, "Catalog 19001").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZotUzpY9RZt9JDsEJyrWS
…n-cubic-3315

fix(trust-portal): precise, bounded ISO cert badge matching (cubic review, #3315)
@vercel vercel Bot temporarily deployed to staging – portal July 1, 2026 15:11 Inactive
@vercel vercel Bot temporarily deployed to staging – app July 1, 2026 15:11 Inactive
tofikwest and others added 2 commits July 1, 2026 11:13
…he root page

cubic review (CS-569, P2): the centralization refactor made the root page
short-circuit offboarded users to /auth/access-removed before the /setup
passthrough, so an explicit ?inviteCode= on / was swallowed for offboarded
users — a regression from the prior behavior where 0-org users fell through
to /setup (which hands the code to /invite downstream).

Restore invite-first precedence: when ?inviteCode= is present, redirect to
/setup (preserving the code) so the downstream /invite handling runs, before
applying the offboard guard. Mirrors the /setup route precedence.

Adds a regression test: offboarded + ?inviteCode= -> /setup (not access-removed).

Refs: CS-569

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6zcF9bvPP1UwZgzK21cw1
…ing-loop-guard

fix(auth): stop offboarded users from looping into a spurious new org (CS-569)
@vercel vercel Bot temporarily deployed to staging – portal July 1, 2026 15:19 Inactive
@tofikwest tofikwest merged commit a5f6352 into release Jul 1, 2026
12 checks passed
@claudfuen

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.94.2 🎉

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