Skip to content

[dev] [tofikwest] feat/security-questionnaire-extension - #3522

Closed
github-actions[bot] wants to merge 16 commits into
mainfrom
feat/security-questionnaire-extension
Closed

[dev] [tofikwest] feat/security-questionnaire-extension#3522
github-actions[bot] wants to merge 16 commits into
mainfrom
feat/security-questionnaire-extension

Conversation

@github-actions

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

Copy link
Copy Markdown
Contributor

This is an automated pull request to merge feat/security-questionnaire-extension into dev.
It was created by the [Auto Pull Request] action.


Summary by cubic

Ships the Chrome security questionnaire extension and updates API CORS/origin policy to safely allow it. Adds org‑scoped answer saving and a release workflow for publishing.

  • New Features

    • Chrome extension @trycompai/security-questionnaire-extension: popup and side panel to detect questions, generate answers, approve, and insert; inline buttons; Google Sheets mapping with direct API insert (optional) or guided paste; per‑domain workspace confirmation and org switching.
    • API CORS/origin policy: new route‑scoped allowlist for extension origins via COMP_EXTENSION_TRUSTED_ORIGINS; wildcard domains restricted to HTTPS; preserves HEAD; adds Vary headers; only allows the extension on GET /api/auth/get-session, GET /v1/auth/me, POST /api/auth/organization/set-active, POST /v1/questionnaire/answer-single.
    • Questionnaire API: AnswerSingleQuestionDto documented in Swagger; generated answers are saved with organizationId and validated against the questionnaire’s org.
    • Release: GitHub workflow to build and publish the extension to the Chrome Web Store using a service account, with publish_target (trusted testers or public).
  • Migration

    • Set COMP_EXTENSION_TRUSTED_ORIGINS=chrome-extension://<extension-id> in API env (staging/production). Keep AUTH_TRUSTED_ORIGINS as before.
    • For local dev: in the extension .env, set WXT_PUBLIC_API_BASE_URL and WXT_PUBLIC_APP_BASE_URL; optionally set WXT_EXTENSION_KEY for a stable ID and WXT_GOOGLE_OAUTH_CLIENT_ID to enable direct Sheets API insertion.
    • Redeploy the API to pick up the new CORS middleware. No DB migration required.

Written for commit d63c6cc. Summary will update on new commits.

Review in cubic

tofikwest and others added 16 commits June 8, 2026 19:15
- Use a closed shadow root for the domain confirmation dialog so the host
  page cannot reach its controls.
- Escape leading formula characters when building Sheets paste TSV, so
  answers starting with "-" stay text instead of being evaluated.
- Restrict the wildcard trusted-origin match to HTTPS; explicit localhost
  entries are unaffected.
- Stop generate-all from overwriting approved or manually edited answers.
- Hold the inline preview locally so concurrent generates cannot render a
  result into another field's preview.
- Finalize queue item status when single generation fails, instead of
  leaving the item stuck in "generating".
- Implement the Google Docs footer copy action, which had no handler, and
  label/enable the button to match.

Adds coverage for the paste plan, batch candidate selection, docs footer,
clipboard text, and the origin policy.
…view-fixes

# Conflicts:
#	apps/api/src/auth/auth.server.ts
#	bun.lock
…release, add docs

The release workflow required a Google OAuth client id and failed the build
when the manifest had none, so the extension could not ship without direct
Google Sheets API access. Treat it as optional: verify the manifest only
when the secret is set, and drop it from the required-secret check.

Adds the extension documentation page and links it from the docs nav.
Detection and data correctness:
- Stop dropping questionnaire rows whose text looks like a column label.
  Any cell under 48 characters containing "control", "requirement" or
  "question" was discarded, which silently lost common questions such as
  "Are security controls documented?".
- Ignore answer-style statements in the keyword fallback so filled-in
  answers are not re-detected as new questions.
- Reject row/column 0 when parsing sheet targets; A1 notation is 1-based.
- Reject negative sheet ids when building formatting requests.

Concurrency and data loss:
- Apply batch results to the stored queue rather than a snapshot, and skip
  items the user edited or approved while generation was in flight.
- Serialize storage read-merge-writes so concurrent updates are not lost.
- Keep the sheet mapping dialog populated when a save fails.
- Surface autosave failures instead of dropping them.

Robustness:
- Guard JSON parsing in the API client, GViz table parsing and gid decoding.
- Let one Sheets endpoint failure fall through to the next.
- Fall back to local detection when the background message fails.
- Add Vary headers to CORS responses, including rejected origins.
- Trim organization ids and reject empty question ids and whitespace-only
  approvals.
- Tighten isItemResponse to validate the queue it claims to carry.

Cleanup: drop the unused ReviewPanel and the unhandled
comp:generate-visible-page message, dedupe getHost and surface validation.
The replacement CORS middleware dropped HEAD, which app.enableCors allowed
by default, so cross-origin HEAD preflights would have started failing.
… the working API

Switches Chrome Web Store auth from a personal refresh token to a service
account JWT, which does not expire and is not tied to an individual account.

Verified against the live API: the token exchange succeeds and the service
account can read the store item. Every v2 endpoint returns 404 for this
publisher while v1.1 works with the same token, so the upload, status and
publish calls now use v1.1. That API is supported until 2026-10-15.

Adds a publish_target choice so a manual run can ship to trusted testers
before going public.
… batch generation

A rejected save left the serialized write chain permanently rejected, so
every answer generated afterwards was computed and then silently dropped.
Keep the chain alive, collect per-item write failures instead of throwing
from a pool worker (which abandoned the questions queued behind it), and
report them once the batch has persisted everything it could.
The check embedded a JS template literal inside a double-quoted shell
string, so bash tried to expand ${process.env.VERSION} and aborted the
step with 'bad substitution'. The release workflow has never run, so this
would have failed on the first publish. Use string concatenation instead.
…ission

Every activeTab occurrence in the source is a local variable name; the
permission itself is never relied on, and the <all_urls> host permission
already covers the same access. The store rejects versions that request
permissions they do not need.

Also refuses to publish a package whose manifest still points at
localhost, which is what the build produces when the public URL env vars
are missing.
When a queue write failed the question stayed marked `generating`, and
that status is excluded from batch candidates — so every later Generate
All skipped it and the answer could never be recovered. Reset the item to
pending after a failed write.
…ailed write

Resetting a failed question to pending discarded whatever state it had
before the batch, so a transient storage error downgraded a question that
already held a generated or flagged answer. Restore the status captured
before the batch marked it generating.
The CORS layer was rewritten for the extension, so assert the four origin
classes that actually reach production — app, portal, arbitrary trycomp.ai
subdomain, and a customer custom domain — still receive the right headers,
and that resolution does not depend on AUTH_TRUSTED_ORIGINS, which the API
is not given in production.
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jul 28, 2026 6:54pm
comp-framework-editor Ready Ready Preview, Comment Jul 28, 2026 6:54pm
portal Ready Ready Preview, Comment Jul 28, 2026 6:54pm

Request Review

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.

1 participant