Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/specs/guarded-approval-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Guarded approval data contract

This additive core contract describes approval context and approval commands. It contains no HTTP client, authentication implementation, storage, automatic retry or legacy approval fallback. Existing authoring contracts remain unchanged. A server must support the guarded actions; this document does not claim that a particular deployment does.

## Requests

A scope has explicit `projectId`, `environment` and `locator`. The locator contains `entityKey`, `draftId`, `key`, `locale` and optional `variantId`. Identifiers are nonempty trimmed strings, preserved exactly. Project is at most200 characters; environment is at most100 and matches `^[a-z0-9][a-z0-9-]*$`; locator strings are at most1024. A supplied variant cannot be empty. The entity key equals the URI-encoded `authoring`, environment, locale, key and draft ID segments joined by `#`.

`createLingvaGuardedApprovalContextPayload(scope)` returns `{action:'review-context',projectId,environment,locator}`.

`createLingvaGuardedApprovalPayload({...scope,contextToken,operationId})` returns `{action:'review-mutate',command:'approve',projectId,environment,locator,contextToken,operationId}`. The context token is opaque lowercase64-hex. The operation ID is a caller-supplied UUID, version1–8, with its exact case preserved. Callers retain one captured command and ID for uncertain-response recovery; a changed context or target is a new intent. The builder never generates an ID or fetches context.

Request inputs allow only the fields described here. Actor overrides, credentials, transport headers, URLs, comments and assignments are not request fields. Invalid input throws `TypeError('Invalid guarded approval request.')` without input values. Serialized requests may not exceed16KiB UTF-8. A rejected guarded request never becomes legacy approval.

## Responses

`parseLingvaGuardedApprovalContextResponse(value,expectedScope)` returns `{ok:true,context}` with a fresh known-field projection, or `null`. Context contains matching scope, server-reported `actorId`, context token, boolean `isCurrent`, and:

- `draft:{value,status,authorId?,approvedBy?,approvedAt?}`, status `draft|approved`;
- `review:{revision,assigneeId?,outcome?}`, nonnegative safe revision, outcome `approved|changes-requested|rejected` when present;
- `baseline:null|{revision,publishId}`;
- `permissions:{comment,assign,decide}`, each boolean. A noncurrent context has no true permission flag.

Required identifiers use the bounded string rules. Timestamp fields represent a finite instant with explicit timezone. Locator equality compares field values, independent of insertion order. Missing facts are not defaulted. Draft content remains exact, including whitespace; validation errors do not include it. Additive unknown response properties are omitted, not promoted into authority. Decoded responses may not exceed128KiB UTF-8.

`parseLingvaGuardedApprovalResponse(value,expectedOperationId)` returns `{ok:true,result}` or `null`. An approval result requires the exact expected UUID string, positive safe `reviewRevision`, `outcome:'approved'` and valid `approvedAt`. No actor/target attestation or fabricated draft record is added. Unsupported/error/malformed responses remain failures.

These parsers describe supplied JSON observations. They do not verify the server, principal, freshness or permissions. A context token is not a bearer credential. An acknowledgment does not promise indefinite receipt retention or an immutable audit archive. Authorization remains a server responsibility, including admission before retry recovery.

## Portability and compatibility

The codecs are portable pure functions without network, environment, random, clock or storage access. Legacy authoring actions and serializers retain their existing behavior. Other review commands, history, assignment, transport helpers, CLI behavior and server mutation logic are outside this contract. Fixtures and package smoke examples use synthetic content only.
19 changes: 19 additions & 0 deletions docs/specs/workspace-dependency-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Workspace dependency security repair

The workspace dependency audit fails against the existing lockfile. Repair the dependency owners and verify local compatibility while preserving all Lingva package versions, licenses and guarded approval codec sources. No package publication, hosted operation or deployment belongs to this repair.

Use published patched versions: Next16.4.1, Nuxt4.6.2, Vite8.1.6, launch-editor2.14.2, Zod4.6.6, Undici8.12.2, serialize-javascript7.1.3, fast-uri3.1.9 and seroval1.6.11. Keep transitive security overrides in `pnpm-workspace.yaml`, scoped to the affected major where necessary. Regenerate the lockfile through pnpm and prove a frozen installation.

The SvelteKit starter moves to3.0.0, adapter-auto8.0.0, Svelte>=5.57.1 (locked5.57.3) and TypeScript6.0.3. Only that starter requires TypeScript6. Use explicit `#lib/*` subpath imports and the relocated hooks type without changing locale logic. Move its existing preprocess/adapter options from `svelte.config.js` into the Vite plugin; use `$app/tsconfig` and explicit include/exclude paths. Preserve locale selection, SSR output and public SDK interfaces. Follow the [official migration guide](https://github.com/sveltejs/kit/blob/main/documentation/docs/60-appendix/35-migrating-to-sveltekit-3.md).

Deepmerge4.3.1 has no patched release for [GHSA-gcqx-w48m-w2h4](https://github.com/advisories/GHSA-gcqx-w48m-w2h4). Apply a local defensive patch following the [reporter's key-guard recommendation](https://github.com/TehShrike/deepmerge/issues/273): reject `__proto__`, `constructor` and `prototype` in both target and source copy paths. Patch source and both shipped CJS/UMD artifacts. This is a local mitigation, not a claimed upstream release or maintainer patch. Preserve ordinary object, array, symbol and custom-merge behavior.

Prove the exploit with real installed CJS/UMD entrypoints before patching, then prove rejection and compatibility afterward. Add an unconditional local dependency-security test command to repository CI checks. Only after the installed patch passes may the exact deepmerge advisory receive a documented registry-audit exception, because the registry sees the original package version. Existing braces/node-forge exceptions and the node-forge patch remain unchanged. Never suppress other advisories or skip the audit.

Acceptance: no unsuppressed production audit findings, passing exploit/compatibility tests and frozen install, unchanged codec source hashes and250 core tests, full affected workspace lint/types/tests/builds, all11 public build/tarball consumer checks, and real local HTTP checks for the changed Next/Nuxt/SvelteKit starters. Keep one existing PR; report any remaining blocker honestly.

Rollback is a Git revert of this repair's commit followed by frozen installation. It restores the previous manifests, lockfile, configuration and patch policy together; it also restores their known audit failures. No persistent application data is migrated.

For the Nuxt starter, production builds run `nuxt typecheck` before bundling. The duplicate Vite checker is disabled because its shell invocation splits absolute project paths containing spaces. Standalone type checks remain available; the in-dev checker overlay is not enabled by this repair.

Local verification on29 October2026:23 installed-artifact/graph tests,250 core tests,70 workspace tasks,19 builds,11 public candidate consumer checks and10 production HTTP scenarios pass. Frozen installation and peer checks pass. Registry audit exits0 with no active advisory objects; its raw metadata still counts the two explicitly ignored patched node-forge/deepmerge findings. This is not a claim of an exception-free audit. Lingva package manifests, codec sources and licenses match the pre-repair source.
57 changes: 57 additions & 0 deletions docs/superpowers/plans/2026-10-29-guarded-approval-codecs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Guarded Approval Codecs Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add portable, additive guarded approval request/response codecs to core without changing existing callers.

**Architecture:** One focused module owns types, named selectors, deterministic request builders and sanitized response parsers. A data-only UTF-8 JSON budget check prevents custom serialization hooks from bypassing bounds. The existing public index exports the new surface; package smoke tests exercise the packed result.

**Tech Stack:** Existing TypeScript/Vitest/build-package tooling and portable TextEncoder; no new dependency.

**Spec:** `docs/specs/guarded-approval-contract.md`.

## Global Constraints

- Core only: no HTTP helper, CLI/adapter, server, storage, permission, retention, release or deployment changes.
- Keep legacy actions/types/serializers and release pins unchanged. Add a separate guarded selector/type boundary.
- Request16KiB and response128KiB UTF-8 limits include full JSON data, including unknown response data; never invoke custom toJSON/accessor hooks to determine size.
- Explicit project/environment/full locator; preserve exact identifiers, variant and operation UUID case. Context is opaque64 lowercase hex; UUID versions1–8.
- Return fresh recognized-field projections and null for invalid responses. No missing-fact defaults, actor overrides, fallback, I/O or credential access.
- Public source/spec/fixtures contain public API data only. Keep source-map protection and existing license files unchanged.

## Review Focus

- UTF-8 multibyte and JSON escape boundaries must use serialized byte size, not JavaScript string length.
- Field-order differences, variants and encoded entity keys must not silently cross scope.
- Unknown fields, malformed timestamps and unsafe revisions must not become permission or identity claims.
- Accessor/toJSON/cycle-shaped non-JSON inputs must not execute application serialization hooks or bypass byte limits.
- Packed ESM/CJS/type exports must expose the same additive API while all legacy tests and callers remain unchanged.

---

### Task 1: Implement, test and export the codecs

**Files:** Create `packages/core/src/guarded-approval.ts` and `.test.ts`; modify `packages/core/src/index.ts` with an additive named export block. Extend `scripts/verify-public-packages.mjs` only for guarded-core smoke assertions. Update this spec/plan as completed.

**Interfaces:**

- `LingvaGuardedApprovalLocator`, `LingvaGuardedApprovalScope`, `LingvaGuardedApprovalCommand` describe validated request data.
- `LINGVA_GUARDED_APPROVAL_ACTIONS` contains context=`review-context`, approve=`review-mutate` only; command is fixed `approve`.
- `createLingvaGuardedApprovalContextPayload(scope): LingvaGuardedApprovalContextPayload` and `createLingvaGuardedApprovalPayload(command): LingvaGuardedApprovalPayload` throw the spec's static TypeError on invalid input.
- `parseLingvaGuardedApprovalContextResponse(value:unknown,expectedScope): LingvaGuardedApprovalContextResponse|null` and `parseLingvaGuardedApprovalResponse(value:unknown,expectedOperationId): LingvaGuardedApprovalResponse|null` return full sanitized success envelopes.
- Public context/result types mirror only the spec's fields. Context observation flags never authenticate or authorize anything.

- [x] **Step1: Baseline and failing behavior tests.** Run full core baseline. Write literal payload/context/ack expectations using project `project_demo`, environment `dev`, locator `{entityKey:'authoring#dev#en#hero.title#draft_one',draftId:'draft_one',key:'hero.title',locale:'en'}`, token64`a`, UUID `11111111-1111-4111-8111-111111111111`. Cover every request/response field, limits, scope/variant mismatch, UUID case, known optional fields, additive stripping, exact timestamps/revisions, multibyte bytes/escapes and no ambient access. Tests name the wrong branch/omitted guard/unsafe echo they catch; expectations are literals.
- [x] **Step2: Observe assertion RED.** Export a temporary interface scaffold returning empty payloads/null so tests fail via actual expected-value assertions, not import errors. Run `pnpm --filter @lingva/core test src/guarded-approval.test.ts`; retain failure output. Replace the scaffold only after observing failures.
- [x] **Step3: Minimal implementation.** Validate own JSON data fields and scope; construct known output shapes. Use one internal iterative JSON byte-budget traversal with active-path cycle checks and safe primitive encoding, no toJSON/getter invocation; stop on overflow/non-JSON data. Parsers copy only known validated values and compare scope by fields. Timestamp validation uses deterministic input only. Public constants/types remain separate from legacy authoring.
- [x] **Step4: GREEN and static quality.** Focused/full core tests, core lint/typecheck/build all pass. Test accessor/toJSON and byte-budget mutations through observable outputs. Add root exports; do not edit legacy authoring or client sources. Review changed paths and dependency/lock/license hashes.
- [x] **Step5: Candidate and public export gates.** Extend existing packed ESM/CJS/type smoke with literal guarded context/mutation examples and typed results. Run existing `pnpm build:packages` then `pnpm verify:packages`; install only local candidate tarballs with temporary overrides/ignore-scripts as the existing harness does. Record candidate hashes separately; never publish or commit temporary file overrides. Validate packaging smoke RED against a pre-change candidate when practical; at minimum the codec assertion RED is mandatory and final packed exports must be exercised.
- [x] **Step6: Commit and task completion.** Record actual local results and source hashes; run task-done with the full core test command. Keep any implementation rulings in the ignored plan ledger. No merge or release claim.

## Final review and handoff

Request one fresh whole-branch technical review against the spec/Review Focus and exact base/head. Resolve Important/Critical findings in one RED/GREEN pass; record genuine Minor deferrals. Re-run affected/full gates after source changes. Preserve source-map and public-package boundary checks. Archive the plan's owned ignored scratch, then remove only that scratch after review completion; retain the Git worktree for feedback. Open one SDK PR only, with public scope/validation and no private tracking data. Stop for review; do not merge or publish packages.

## Self-review

One cohesive codec surface owns all requested behavior; no cross-task interfaces. Every contract clause maps to Task1. Public exports are verified from tarballs, and all five Review Focus risks have behavior cases. Native inline execution and one final independent review are the chosen workflow; no additional runtime or generic review feature is implied.
48 changes: 48 additions & 0 deletions docs/superpowers/plans/2026-10-29-workspace-dependency-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Workspace Dependency Security Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Repair the existing dependency-audit blocker with reproducible patched dependencies and local compatibility proof.

**Architecture:** Existing workspace override policy owns transitive version floors. Direct starter manifests own framework upgrades. One narrowly scoped deepmerge patch and installed-artifact regression suite cover the dependency with no fixed release.

**Tech Stack:** Existing pnpm12.8.1, Node24, node:test, workspace/Turbo builds and public package consumer harness.

**Spec:** `docs/specs/workspace-dependency-security.md`.

## Global Constraints

- Preserve Lingva versions, licenses, public contracts and all guarded approval codec source bytes; no hosted operations, publication or deployment.
- Use the spec's exact published patched versions and existing installation/build policy. Keep TypeScript6 scoped to the SvelteKit starter.
- Patch deepmerge source/CJS/UMD and prove real exploit RED/GREEN before a single documented exact-advisory exception. Existing exceptions and node-forge patch remain unchanged.
- Keep the existing PR; do not open a successor. Roll back all repair inputs together through Git and frozen install.

## Review Focus

- Poisoned keys on source, target, null-prototype objects and nested placeholders must never change the returned object's prototype.
- Shipped CJS and UMD artifacts must both receive the patch; a registry exception must not outlive its effective installed mitigation.
- Ordinary arrays, symbols, custom merge callbacks and nonmutating merge behavior must remain compatible.
- SvelteKit3 configuration and TypeScript6 must preserve real SSR locale selection without leaking changed toolchain requirements into public SDK packages.
- Frozen installation, all workspace gates and packed consumers must exercise the actual changed dependency graph without disabling audit or source-map/license protection.

---

### Task 1: Repair and verify the workspace dependency owners

**Files:** Modify `pnpm-workspace.yaml`, generated `pnpm-lock.yaml`, `package.json`, `scripts/ci-run-workspace.mjs`, Next/Nuxt/SvelteKit starter manifests and SvelteKit configuration; remove its obsolete `svelte.config.js`. Create `patches/deepmerge@4.3.1.patch` and `scripts/dependency-security.test.mjs`. Update these public documents with actual evidence after verification.

**Interfaces:** Existing pnpm frozen install, `pnpm audit:prod`, new `pnpm test:dependency-security`, existing workspace lint/typecheck/test/build and public tarball consumers. No Lingva runtime API changes.

- [x] **Step1: Establish RED.** Save fresh audit JSON and exact registry metadata. Add real installed CJS/UMD tests for poisoned source/target keys, null prototypes and nested placeholders, plus ordinary array/symbol/custom-merge compatibility. Run `node --test scripts/dependency-security.test.mjs`; expected real prototype/key assertion failures, with ordinary compatibility controls passing.
- [x] **Step2: Apply narrow repairs.** Update exact owner versions and scoped overrides, migrate only SvelteKit starter configuration using the official guide, and generate the deepmerge patch through `pnpm patch`/`patch-commit`. Preserve dependency installation policy and all Lingva source/version/license inputs. Expected frozen install and exploit/compatibility GREEN.
- [x] **Step3: Connect and verify the audit boundary.** Add the installed-artifact test command to existing repository checks. Document only the exact deepmerge patched-advisory exception after GREEN. Run frozen install and `pnpm audit:prod`; expected zero unsuppressed findings under the explicitly recorded exception policy.
- [x] **Step4: Verify compatibility.** Run full workspace lint/types/tests/build, all11 public package builds/tarball consumers, and local HTTP locale smoke for Next/Nuxt/SvelteKit output. Compare codec/license/version hashes with basebf4228b. Expected all relevant gates pass and no source/version/license drift.
- [x] **Step5: Record and commit.** Mark actual checks, commit the coherent repair, and run task-done with `pnpm test:dependency-security`. Expected23 installed-artifact/graph tests pass; no merge or release claim.

## Final review and handoff

Obtain one fresh whole-branch review of this repair after task completion; fix Important/Critical findings in one RED/GREEN pass and ledger genuine Minor deferrals. Update the existing PR and private tracking only. Archive and remove only this plan’s owned ignored scratch; preserve the worktree for review.

## Self-review

One dependency graph repair owns the framework compatibility and patch tests together. Each review risk has an installed-artifact or real starter check. Existing codec acceptance remains a separate immutable source comparison. The native workflow continues under the user's instruction to proceed; no extra implementation-method gate is introduced.
2 changes: 1 addition & 1 deletion examples/next-starter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@lingva/core": "workspace:*",
"@lingva/next": "workspace:*",
"@lingva/react": "workspace:*",
"next": "16.3.8",
"next": "16.4.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
Expand Down
Loading
Loading