Skip to content

[API][Web] Partner/org Website field has no URL or scheme validation — accepts javascript: and data: URIs #3430

Description

@ToddHebebrand

Description

The partner/org Website field accepts any string, including javascript: and data: URIs. There is no scheme validation and no URL validation of any kind:

// apps/api/src/routes/orgs.ts:482
website: z.string().optional()

Entered via Settings → Partner → Company. Saved and returned verbatim.

Exploitability today: none that I could find — but that is a property of the current UI, not of the data

The QA sweep looked for a renderer and found no <a href={...website}> consuming this field, so there is no live stored-XSS path right now. I am reporting it as a missing control, not a live vulnerability, and deliberately not claiming more than that.

Two reasons it still matters:

  1. The guard belongs at the boundary, not at each renderer. A field named website is going to end up in an href eventually — that is what it is for. Whoever adds that link will reasonably assume the stored value is a URL, because the field is called Website. Note there is already an href={branding.websiteUrl} at apps/web/src/components/software/BuiltinPackageDetail.tsx:64; I did not confirm whether that value can trace back to this field, and that trace is worth doing as part of the fix.
  2. The repo already has the pattern. packages/shared/src/validators/remoteAccessLauncherScheme.ts exists precisely to allowlist schemes, with tests. This field should use the same approach rather than inventing one.

The release checklist for this cycle explicitly asked that Partner settings reject javascript:/data:, which is how this surfaced.

Proposed fix

Validate as a URL with an http/https allowlist, in packages/shared/src/validators/ so both the API schema and the web form can share it, following the remoteAccessLauncherScheme precedent. Reject rather than silently strip — a silently-cleared field looks like a save failure.

Worth grepping for sibling fields while in there (supportUrl, logoUrl, documentationUrl, any other z.string() that names a URL) — this is likely a class, not a single field.

Affected files

  • apps/api/src/routes/orgs.ts:482
  • apps/web/src/components/settings/PartnerCompanyTab.tsx
  • Precedent to follow: packages/shared/src/validators/remoteAccessLauncherScheme.ts

Reported By: internal Playwright UI QA sweep 2026-08-11, evidence in docs/testing/FEATURE_TEST_LOG.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions