ec27228a8e
Em dashes (—) and en dashes (–) had spread across comments, docs, tests, and a few UI strings, reading as AI-generated boilerplate rather than house style. Replaced each with punctuation matching its context: colon for explanatory clauses, comma for asides, plain hyphen for numeric/legal ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for paired-dash asides. messages/en.json and messages/sv.json were fixed by hand together to keep sv/en in sync. Left untouched where the dash is the functional subject rather than decorative punctuation: date-range-parser.ts's separator regex, charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the agent system-prompt files that already instruct against em dashes, and a golden iXBRL test fixture compared byte-for-byte. Also fixes two bugs surfaced along the way: an off-by-one in ApiKeysPanel's scope-label split (a leftover from an earlier partial pass), and a charset-repair test that had lost the literal en-dash it exists to verify. Regenerated the agent atom seed migration (skills:generate) since 27 SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes, with an explicit carve-out for the functional-dash cases above. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
/**
|
|
* Decide whether an `/api` request should SKIP the middleware MFA (AAL2) gate.
|
|
*
|
|
* Most `/api` routes historically hand-roll `supabase.auth.getUser()` instead
|
|
* of `requireAuth()`, which means they never enforce MFA. The middleware gate
|
|
* (lib/supabase/middleware.ts) closes that gap for cookie sessions, but a few
|
|
* request classes must NOT be gated:
|
|
*
|
|
* - Bearer-authenticated SURFACES (`/api/v1/*` API keys, the MCP endpoint's
|
|
* OAuth tokens/API keys): the route validates the Authorization credential
|
|
* itself and never trusts the cookie session, so a logged-in AAL1 browser
|
|
* testing its own API key must not be blocked. This is scoped by PATH, not
|
|
* header presence: the header is attacker-controlled, and an Authorization
|
|
* header riding on a cookie-authenticated route must never disable the
|
|
* gate (the route would ignore the header and authenticate via cookies,
|
|
* i.e. a stolen-password session could bypass MFA with `Authorization: x`).
|
|
* Pure Bearer callers elsewhere (cron secret, signed webhooks) carry no
|
|
* cookie session, so the gate: which only fires for cookie users: never
|
|
* touches them and they need no exemption.
|
|
* - The AAL1 escape hatch: a user with MFA required but not yet verified (or a
|
|
* BankID-only user setting a first password) must still reach
|
|
* `/api/account/*` and `/api/company*` to COMPLETE onboarding / enroll MFA.
|
|
* - The MCP OAuth endpoints (`/api/mcp-oauth/*`) carry their own PKCE +
|
|
* single-use-code security and drive the connector authorize flow.
|
|
*
|
|
* Kept as a pure function so the allowlist is unit-testable in isolation.
|
|
*/
|
|
|
|
// Routes whose auth contract IS the Authorization header. Everything else
|
|
// under /api/extensions/ext/ authenticates via requireAuth (cookies) in the
|
|
// dispatcher and must stay behind the gate.
|
|
const BEARER_AUTH_PREFIXES = ['/api/v1/', '/api/extensions/ext/mcp-server/mcp']
|
|
|
|
export function apiPathSkipsMfaGate(
|
|
pathname: string,
|
|
hasAuthorizationHeader: boolean,
|
|
): boolean {
|
|
if (
|
|
hasAuthorizationHeader &&
|
|
BEARER_AUTH_PREFIXES.some((prefix) => pathname.startsWith(prefix))
|
|
) {
|
|
return true
|
|
}
|
|
return (
|
|
pathname.startsWith('/api/account/') ||
|
|
pathname.startsWith('/api/company') ||
|
|
pathname.startsWith('/api/mcp-oauth/')
|
|
)
|
|
}
|