feat: gap fill — bundle guards + SQL linter + passkeys + OTel + post edit screen - #484
Merged
Merged
Conversation
The .gnplugin loader at cli/gonext/internal/plugintest accepted
arbitrarily-large archives and never validated entry names. A
hostile bundle could:
* exhaust process memory with a multi-gigabyte file or a zip-bomb
entry (small compressed, huge declared size);
* exhaust the file-descriptor pool with a million zero-byte
entries;
* extract files outside the bundle root via "../" or "/etc/..."
paths (zip-slip, https://snyk.io/research/zip-slip).
This change adds three independent caps — 50 MiB total, 10 MiB
per entry, 10,000 entries — plus a path-safety check that rejects
any entry whose raw name starts with "/" or contains a ".."
segment in either / or \ form.
Each cap has a dedicated sentinel error (ErrBundleTooLarge,
ErrBundleEntryTooLarge, ErrBundleTooManyEntries,
ErrBundleUnsafePath) so the admin UI can branch on the failure
shape and produce a friendlier message.
Read paths (ReadManifest, ReadWASM) now also pass through a
LimitReader-backed cap so directory-form bundles (which have no
central directory to pre-validate) are equally protected.
Closes #27.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Plugin-supplied migrations run inside the same database as core GoNext, so a careless or malicious plugin could drop core tables, shadow core relations, or rewrite core indexes. Per docs/02-plugin-system.md §3.3, every DDL statement in a plugin's migrations must target an object whose name begins with `plugin_<slug>_`. This change adds a regex-based static linter (packages/go/plugins/lifecycle/sqllint.go) that enforces the prefix on CREATE/ALTER/DROP TABLE, CREATE/DROP INDEX, and rejects the forbidden-verb kill-list (CREATE FUNCTION/VIEW/EXTENSION/ TRIGGER, GRANT, TRUNCATE, ALTER SCHEMA, ...). Schema qualifiers and quoted identifiers are stripped before the check so `CREATE TABLE public."plugin_seo_x"` passes while `CREATE TABLE users` is rejected. Wire-up into the Migrator runner lands in a follow-up — this PR ships the linter as a standalone function so the bundle-parser issue (#27) and the lifecycle storage (#44) can plug it in without coupling. Closes #53. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
apps/api/cmd/server/main.go imported otelhttp transitively but never
initialised a tracer provider, so the otelhttp middleware spans
silently fell through to the global no-op tracer. This change adds a
packages/go/tracing package that:
* reads `GONEXT_OTLP_ENDPOINT` (or Options.Endpoint),
* stands up an OTLP HTTP exporter pointed at it,
* wraps it in a batched SDK TracerProvider with the binary's
`service.name` / `service.version` resource attributes,
* installs the W3C TraceContext + Baggage composite propagator
globally so incoming `traceparent` headers thread through every
downstream client,
* returns a Shutdown closer the binary registers with the
shutdown orchestrator for graceful flush.
When the env var is unset the package returns a no-op Shutdown but
still installs the W3C propagator — partial rollouts where this
service has tracing disabled but its neighbours don't will still
propagate trace IDs end-to-end.
main.go wires the whole mux through otelhttp.NewHandler with a
custom SpanNameFormatter that prefers the matched ServeMux pattern
over the raw URL path, keeping span-name cardinality bounded across
the /api/v1/posts/{id} family.
Closes #186.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
GoNext's auth surface shipped password / TOTP / OAuth but had no
phishing-resistant credential. This change adds end-to-end
WebAuthn / passkey support:
* packages/go/auth/webauthn — Service wrapping the upstream
go-webauthn library, MemoryStore for tests + the boot-time
in-memory dev loop, Record/User adapter types.
* apps/api/internal/auth/webauthn — four HTTP routes wiring
the ceremony (register/begin, register/finish, login/begin,
login/finish) plus the admin list+delete surface
(GET/DELETE /api/v1/auth/webauthn/credentials[/{id}]). The
handler enforces row-level ownership before delete and
rejects discoverable-login probes.
* migrations/000035_webauthn_credentials — new
webauthn_credentials table with a unique index on
credential_id (assertion lookup) and a per-user index for
the admin list.
* apps/admin /settings/account — page with the PasskeyList
component, an "Add passkey" form that drives
navigator.credentials.create + .get, and per-row Remove.
Pasted into the settings overview as a fifth card.
The handler keeps ceremony state in a separate SessionStore
(Redis in prod, in-memory in tests) keyed by a random 16-byte
ceremony id; the user id is part of the key so a stolen
ceremony id from user A cannot be replayed against user B.
Closes #159.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
The post-detail page shipped as a metadata-only stub — no block
editor, no autosave, no PATCH wiring. This change turns it into a
real edit screen:
* BlockEditCanvas (from @gonext/blocks-editor) renders the
content_blocks tree with the paragraph + heading core blocks
registered via defaultCoreBlocks. The editor takes its own
"Add your first block" affordance when the tree is empty.
* useAutosave wires the canvas to /api/v1/posts/{id}/autosave
with the package defaults (30s interval, 1.5s debounce). The
page-head row carries a tiny AutosaveStatusPip the user can
glance at to confirm their work is safe.
* The "Save changes" button issues a PATCH to /api/v1/posts/{id}
with the current title / slug / status / content_blocks; the
handler returns the freshly-saved row and our state stays in
sync with the server.
* The settings overview adds a fifth "Account" card so the
passkey-management surface from #159 has a navigable entry
point.
apps/admin/package.json gains @gonext/blocks-editor +
@gonext/blocks-sdk as workspace deps so the import paths resolve
under tsc.
Closes #35.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni
enabled auto-merge (squash)
May 26, 2026 18:23
|
Heads up — this PR touches strings that often signal a security disclosure ( If this PR fixes or describes a real vulnerability that has not yet been publicly disclosed, please stop and use the private path:
See If this is a false positive (test fixture, doc update, release notes, etc.) please ignore this comment — the check is advisory only and does not block the PR. Matched files:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #27, #53, #159, #186, #35.
Summary
cli/gonext/internal/plugintestbundle parser (50 MiB total, 10 MiB per entry, 10 k entries, reject../ absolute paths).packages/go/plugins/lifecyclethat enforces theplugin_<slug>_prefix on every DDL target and rejects a kill-list of dangerous verbs.packages/go/auth/webauthnservice +apps/api/internal/auth/webauthnHTTP routes (register/login begin/finish + admin list/delete) + migration000035_webauthn_credentials+/settings/accountPasskeyList UI.apps/api/cmd/server/main.gowith OTLP HTTP exporter, W3C propagator, otelhttp on the router, graceful shutdown via the orchestrator; no-op whenGONEXT_OTLP_ENDPOINTis unset.BlockEditCanvas+useAutosaveand a Save button that PATCHes/api/v1/posts/{id}.Test plan
go test ./...for the four touched Go packagespnpm testinapps/admin(566 tests pass)GONEXT_OTLP_ENDPOINTset — confirm spans land on the collector/settings/accountand verify the WebAuthn ceremony round-trips🤖 Generated with Claude Code