Skip to content

feat(components): <zl-gate> atom, null-safe invisible atoms, mock gate verification - #159

Closed
peintnermax wants to merge 4 commits into
mainfrom
frontend-gate
Closed

feat(components): <zl-gate> atom, null-safe invisible atoms, mock gate verification#159
peintnermax wants to merge 4 commits into
mainfrom
frontend-gate

Conversation

@peintnermax

Copy link
Copy Markdown
Member

Implements the frontend side of ADR 016 — the in-flow captcha gate contract.

<zl-gate> atom

New invisible Lit component that handles gate proof-of-work. Reads gate config from step attributes, dispatches on kind + provider, solves the challenge, and emits zl-gate-result. The orchestrator collects proofs into a gateProofs map and includes them as gate_proofs in the submit body.

Supported providers: Altcha (built-in PoW), Turnstile, hCaptcha, reCAPTCHA. Third-party widgets mount in light DOM to avoid shadow-DOM iframe issues.

Null-safe invisible atoms

Both invisible auto-acting atoms (<zl-gate>, <zl-passkey>) now follow the same pattern:

  • Always present in templates — no {% if %} guards needed
  • Use firstUpdated() instead of connectedCallback() (Lit reflects attributes after connectedCallback, so config/options would be null)
  • Silently no-op when their data is null; activate when data becomes available

This removes the {% if challenge and challenge.method == "passkey" %} boilerplate from all three Liquid templates. Custom branding authors no longer need to know about invisible atoms.

mandatory-gatesrequired-atoms rename

The patcher injects missing fields, gates, and submit buttons — not just gates. Renamed to reflect its actual scope:

  • File: mandatory-gates.tsrequired-atoms.ts
  • Tag: {% mandatory_gates %}{% required_atoms %}
  • Exports: patchRequiredAtoms, REQUIRED_ATOMS_MARKER, requiredAtomsMarkerComment

Updated across all templates, specs, orchestrator imports, barrel exports, ADR 016, and 5 design docs.

Mock server gate verification

  • packages/api-mock/src/altcha.tsgenerateAltchaChallenge() + verifyAltchaProof() using crypto.subtle
  • packages/api-mock/src/handlers.ts — tracks issued challenges per gate name, verifies proofs on submit (opt-in via verifyGates: true), logs / results
  • packages/api-mock/src/fixtures/login.ts — identifier step includes an Altcha gate (bot_check)
  • Standalone server (server.ts) enables verification; unit tests disable it (default)

ADR 016 updates

  • Status: ProposedAccepted
  • <zl-captcha><zl-gate> (generic name for future gate kinds)
  • gate_proofs values: string → object ({ number, salt } for Altcha, { token } for vendors)
  • mandatory-gatesrequired-atoms

Changed files

Package Files
components/atoms zl-gate.ts (new), zl-passkey.ts (null-safe + firstUpdated)
components/orchestrator required-atoms.ts (renamed), required-atoms.spec.ts, liquid.ts, liquid.spec.ts, zitadel-login.ts, zitadel-login.spec.ts, index.ts
components/templates auth-form.liquid.ts, passkey-upsell.liquid.ts, signed-in.liquid.ts
components src/index.ts, src/manifests.ts, src/manifests.spec.ts
api-mock altcha.ts (new), handlers.ts, server.ts, fixtures/login.ts
docs adrs/016-captcha-gate-and-bot-signals.md, design/branding/*.md (5 files)

Test results

  • 136 tests pass (116 components + 20 api-mock)
  • Build clean

fforootd and others added 4 commits May 26, 2026 10:11
Formalizes the captcha gate contract ADR 013 deferred, and designs a
second bot-detection mechanism for apps behind an edge platform:

- #1 in-flow captcha gate: built-in Altcha (self-hosted PoW) plus
  bring-your-own third-party providers (Turnstile/hCaptcha/reCAPTCHA);
  public site_key in the client-visible gate config, secret server-side;
  verified through the existing auth_attempts challenge/verify path.
- #2 edge/platform signal: the SDK proxy stamps the platform verdict
  (Vercel BotID, Cloudflare managed challenge) as an authenticated inline
  X-Zitadel-Risk-Signal header, trusted via the origin-scoped sk_proj_
  secret; the risk evaluator uses it to suppress or inject the gate.

Also revises bot-detection.md (status -> ADR 016, new platform/edge
signals section, example JSON corrected to the {kind, provider, config}
contract) and adds the ADR index row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
nextgen Ready Ready Preview, Comment May 27, 2026 1:56pm

Request Review

Base automatically changed from adr-016-captcha-gate-and-bot-signals to main May 29, 2026 15:17
@fforootd

Copy link
Copy Markdown
Member

replaced by #562

@fforootd fforootd closed this Jul 21, 2026
fforootd added a commit that referenced this pull request Jul 29, 2026
## Summary

<!-- Briefly describe what changed and why. -->

- Clears all six open Dependabot alerts (5 high, 1 medium — all npm, all
in `pnpm-lock.yaml`). Two are catalog entries and move in
`pnpm-workspace.yaml`; the other four are transitive and land as
range-scoped `pnpm.overrides`, matching the entries already there.
- `liquidjs` 10.27.0 → 10.27.2 — `pop` filter bypassed `memoryLimit`
accounting (CVE-2026-55575, #182).
- `react-server-dom-webpack` 19.2.5 → 19.2.8 — DoS in Server Functions
(CVE-2026-44907, #184); the old version is deprecated on npm.
**`react`/`react-dom` move to 19.2.8 with it**: 19.2.8 peers on
`^19.2.8`, and React ships the RSC packages in lockstep. Without this,
`apps/docs` installs with an unmet peer (see Notes). No
published-contract impact — only the private `@zitadel/ui-react` uses
`catalog:` for its React peers; `sdk-react`/`sdk-next` publish `>=18`.
- `brace-expansion` → 1.1.17 / 2.1.3 / 5.0.8 — unbounded expansion OOM
(CVE-2026-14257, #185). The existing override pinned 5.0.7, which the
new advisory covers, so the bounds move rather than gaining a new entry;
the 1.x and 2.x backports landed 2026-07-28/29.
- `postcss` → `^8.5.18` — `sourceMappingURL` path traversal (#183, and
#181 for the earlier range). Collapses the 8.4.31 copy pinned by
`next@16.2.12` and the 8.5.15 copy from the build tooling into a single
8.5.25.
- `@hono/node-server` → `^2.0.12` — `serve-static` path traversal on
Windows (#159). The vulnerable 1.19.17 comes from
`@modelcontextprotocol/sdk` (via `@fumapress/ai` in `apps/docs`) and the
advisory has no 1.x patch, so the SDK is raised to `^1.30.0` alongside —
1.30.0 widened its range to `^1.19.9 || ^2.0.5`, which keeps the 2.x pin
inside a declared range instead of forcing one.

Supersedes #639, which covered the first two but left
`react`/`react-dom` at 19.2.5.

## Validation

<!-- List exact commands run. If validation was not run, say so
explicitly. -->

- `corepack pnpm install --lockfile-only` — resolves clean. Lock churn
is confined to the affected trees (postcss family, brace-expansion,
`@modelcontextprotocol/sdk`, nanoid); nothing else moved.
- Peer check: no new warnings. The remaining ones
(`nuxt`/`@nuxt/devtools` vs vite 8, `@angular/build` vs vitest 4) are
pre-existing on `main`.
- `corepack pnpm install && corepack pnpm exec moon run :build
:typecheck :test` — 80 tasks, exit 0.

## Release notes / changeset

<!-- State one outcome from the .changeset/README.md decision table
     (.changeset/README.md#decision-table):
     - No changeset required — no shipped behavior changed.
- Changeset: .changeset/<slug>.md — <summary> (list @zitadel/server for
server changes)
- Empty changeset (rare) — <why a publishable path changed but nothing
ships> -->

- Changeset: `.changeset/security-dependency-updates.md` — patch for
`@zitadel/components` and `@zitadel/config`, which ship `liquidjs` as a
runtime dependency. The rest of this PR is build-time or
`apps/docs`-only and ships nothing.

## Notes

<!-- Add reviewer context, follow-ups, risks, or "None". -->

- The peer break #639 would have introduced, reproduced before adding
the `react`/`react-dom` bump:
  ```
  apps/docs
  └─┬ react-server-dom-webpack 19.2.8
    ├── ✕ unmet peer react@^19.2.8: found 19.2.5
    └── ✕ unmet peer react-dom@^19.2.8: found 19.2.5
  ```
- `apps/demo-next` (`react: ^19.0.0`) and `packages/sdk-next` (`>=18`)
are not catalog-managed and stay on 19.2.5 — deliberate, since those
mirror a consumer app's own manifest.
- Overrides go stale silently: a bound like `brace-expansion@>=3.0.0
<5.0.7` stops matching once 5.0.7 is itself the vulnerable version.
Worth a look whenever an alert reappears for a package that already has
an entry.
- Unrelated to this PR, but noticed while checking #639's red run: its
`cli:test` failure was the `release-artifacts.test.ts` assertion
loosened on `main` in 4c3a3ce (#642) a day later, not the dependency
bump.
- There is no `.github/dependabot.yml` in the repo, so only security
updates open PRs — no scheduled version updates. The catalog has drifted
accordingly (72 of 104 entries behind latest: 15 patch, 41 minor, 16
major). Not addressed here.
fforootd added a commit that referenced this pull request Aug 2, 2026
… ADRs (#714)

## Summary

The two deliberately-parked P2 items from the Wicklore remediation plan,
written as **Proposed** ADRs for team alignment — per the plan's rule
that these get no implementation before acceptance
(built-ahead-of-alignment is how PR #56/#159 rotted):

- **ADR 044 — Scaffold Embedding Posture Defaults**: scaffolded
auth/profile pages derive their surface from the fresh-vs-pre-existing
hinge that setup already records (`scaffolded_framework`, ADR 042):
fresh scaffolds keep `variant="page"`, pre-existing apps get
`variant="widget"` in a layout-neutral wrapper so the card stops
fighting the host app's shell. Posture stays a page edit (no config
knob), doctor restores it from the manifest. Open questions flagged: an
explicit `--surface` override, and per-page posture recording.
- **ADR 045 — Copy Overlays as Branding Revisions**: audience copy
(`businessLocales`) is branding-shaped data living in code —
per-app-build, multiplied across eight SDK/template integration points.
Proposal: branding revisions carry per-language copy resolved with the
flow response; `--use-case business` seeds a revision instead of wiring
template props; wording edits follow eject→edit→apply and become
runtime-effective. **Fenced behind the templates-track milestone** —
direction alignment only; the in-flight per-SDK parity wiring stays the
interim mechanism and is explicitly named as transitional.

## Validation

- `node scripts/check-adrs-index.mjs` — 45 records ok.
- Docs-only; no changeset.

## Notes

Stacked on #713 (the index check enforces contiguous ADR numbers, so
this branch carries 043) — the diff collapses to the two proposals once
#713 merges.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants