@massu/core 1.15.3 → 1.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ > **Bundled model attribution.** `@massu/core` ships a pre-trained sentence-embedding
8
+ > model for offline semantic memory recall: **all-MiniLM-L6-v2** (384-dim, int8 ONNX
9
+ > export from `sentence-transformers/all-MiniLM-L6-v2` via `Xenova/all-MiniLM-L6-v2`),
10
+ > redistributed under the **Apache License 2.0**. Full license + NOTICE:
11
+ > `packages/core/assets/embedder/MODEL-LICENSE`.
12
+
13
+ ## [1.15.4] - 2026-07-13
14
+
15
+ ### Security
16
+
17
+ - **Test fixtures are now synthetic.** Five config-migration fixtures were snapshots of real
18
+ private repositories, carrying their project names, stacks and custom verification-type names
19
+ into this package. They are replaced with invented projects, and each fixture directory is now
20
+ named for the migration SCENARIO it pins (`multi-runtime-fastapi-next`, `invalid-router-enum`,
21
+ `twelve-key-passthrough`, …) rather than for a repo it was copied from. A fixture that is a copy
22
+ of a real repository is a leak with a test around it.
23
+ - **CI conditions no longer name a private repository or a maintainer account.** Job gates now
24
+ match positively on the public repo (`github.repository == 'massu-ai/massu'`) instead of matching
25
+ negatively against a private upstream. Same behaviour; nothing private in the expression.
26
+ - **Docs reference the maintainer by role, not by personal account.**
27
+ - **`prepublish-check.sh` now enumerates every GitHub URL in `package.json`** and requires each to
28
+ point at `massu-ai/massu`, replacing a check that grepped for one hardcoded legacy owner slug.
29
+ Strictly stronger: it catches any wrong owner, not only the one that was remembered.
30
+
31
+ No functional or API changes. Every one of these is a docs/fixture/CI-metadata change.
32
+
7
33
  ## [1.15.3] - 2026-07-06
8
34
 
9
35
  **`massu login` hung forever in non-interactive shells (CI / scripts / agents) with `MASSU_API_KEY` set** (`plan-2026-07-06-login-noninteractive-env-hang`). `runLogin()` never read the `MASSU_API_KEY` environment variable and then read a non-TTY stdin unbounded (`for await … process.stdin`, blocking on an EOF that never arrives on an idle non-TTY stdin), so the documented, non-exposing scripted path (`MASSU_API_KEY=<key> massu login`) blocked indefinitely — the only working non-interactive path was `--key`, which leaks the key to shell history. Patch per semver: a bug fix with no API change, scoped entirely to the `login` command. Incident #1.
@@ -684,7 +710,7 @@ Pre-launch comprehensive audit remediation — combined Stage A + Stage B releas
684
710
  - **`website/src/app/api/license/activate/route.ts`** — TWO CRITICAL hardenings in one route refactor: (a) **P-014 atomic UPDATE** — SELECT-check-UPDATE pattern allowed two concurrent requests with the same key to both pass the JS-side status check and double-activate. Replaced with atomic `UPDATE book_purchases SET license_key_status='active', activated_at=NOW() WHERE id=$1 AND license_key_status='inactive' .select('id, activated_at')`. Race losers see `[]` from the post-update select and surface 409 `already_activated`. (b) **P-015 auth + email match** — route was anonymous and resolved identity via `user_profiles.email = purchase.email`, allowing an attacker to sign up as `victim@victim-corp.com` and bind a leaked license key to the victim's org. Now requires `createServerSupabaseClient().auth.getUser()`; returns 401 with `needs_signup` hint (no email leak — anonymous enumeration vector closed) when unauthenticated; returns 403 when `authedUser.email.toLowerCase() !== purchase.email.toLowerCase()`. Both closed in Stage B. Drift-guard: `integration/license-activate.test.ts` covers race-loser, 401 unauth, 403 mismatch, AND case-insensitive matching.
685
711
  - **NEW `website/src/lib/sso-flag.ts`** + GATED `website/src/app/api/sso/callback/route.ts`, `website/src/app/api/sso/route.ts`, `website/src/app/dashboard/settings/sso/page.tsx` — the custom OIDC verifier never cryptographically verified the JWT signature against the IdP's JWKS (P-016 — full SSO bypass for any attacker who registers their own OIDC config), and the custom SAML verifier was vulnerable to XML Signature Wrapping (P-017 — the first-element-wins regex parser allowed outer-wrapper substitution while inner signature still validated). Per operator decision (2026-05-16), SSO is DISABLED via `MASSU_SSO_ENABLED=false` (default) until the library replacement (`openid-client` + `@node-saml/node-saml`) ships in `plan-B.3-followup` post-launch + third-party pen-test. Every SSO entry point returns canonical 503 `SSO_DISABLED`; dashboard SSO settings page shows maintenance UI with `support@massu.ai` contact path. Legacy verifier code preserved below the gate so the followup PR can wire libraries without re-deriving the Supabase user-creation flow. Both closed at the entry point in Stage B. Drift-guard: `website/src/__tests__/sso-disabled-by-default.test.ts` (10 cases) asserts the flag defaults false, that all three entry-point files import + call `isSsoEnabled()`, and that the callback route returns `ssoDisabledResponse()` from BOTH POST and GET handlers.
686
712
  - **`website/src/data/stats.ts`** — added `MCP_TOOL_COUNT` (= 73) named export alongside the existing `stats: Stat[]` SoT, plus `WORKFLOW_COMMAND_COUNT`, `AI_AGENT_COUNT`, `LIFECYCLE_HOOK_COUNT`. Replaced literal `84 MCP tools` claims across 14 TSX/TS surfaces (`app/layout.tsx` 3x, `app/docs/layout.tsx`, `app/features/page.tsx` 4x, `app/about/page.tsx`, `app/overview/page.tsx` 5x, `app/pricing/page.tsx`, `app/checkout/cancel/page.tsx`, `components/sections/Hero.tsx` 2x, `components/sections/OpenSourceSection.tsx`, `components/sections/CloudPreview.tsx`, `components/pricing/PricingFAQ.tsx`, `components/pricing/FeatureComparison.tsx`, `data/articles.ts` 3x, `data/features.ts`, `data/pricing.ts`) with `${MCP_TOOL_COUNT}` interpolation; replaced `84` with literal `73` in 2 MDX content files. Closes P-019 (Stage B). Drift-guard: NEW `website/src/__tests__/marketing-tool-count-against-source-truth.test.ts` walks `website/src` + `website/content` for forbidden literals (`84 MCP`, `84 tools`, `all 84`, plus historical drift literals `47/56/62 MCP/tools`) and asserts zero matches.
687
- - **`.github/workflows/ci.public.yml`** — both `ci.yml` and `ci.public.yml` had workflow `name: CI` and shared `${{ github.workflow }}-${{ github.ref }}` concurrency group; on every main push the stronger `ci.yml` was being CANCELLED while the weaker `ci.public.yml` (4 of 6 internal jobs only — missing Pattern Scanner, Security Scanner, Generalization Scanner, Plan Status Validator, Plan Commit Drift, Plan-Token Changelog Coverage, Website Audit, Hook Build) satisfied branch protection. This is how the 4 API column-drift CRITICALs (P-005..P-008) reached production. Renamed `ci.public.yml` workflow + concurrency group + job-display names to `CI (public-mirror)`; 4 jobs gated `if: github.repository != 'ethankowen-73/massu-internal'` so the internal repo runs the FULL `ci.yml` and the public mirror keeps the public-facing subset. Job-level `name:` values PRESERVED (both rulesets reference them as required status checks). Closes P-020 (Stage A). Drift-guard: NEW `website/src/__tests__/workflow-uniqueness.test.ts` (3 assertions).
713
+ - **`.github/workflows/ci.public.yml`** — both `ci.yml` and `ci.public.yml` had workflow `name: CI` and shared `${{ github.workflow }}-${{ github.ref }}` concurrency group; on every main push the stronger `ci.yml` was being CANCELLED while the weaker `ci.public.yml` (4 of 6 internal jobs only — missing Pattern Scanner, Security Scanner, Generalization Scanner, Plan Status Validator, Plan Commit Drift, Plan-Token Changelog Coverage, Website Audit, Hook Build) satisfied branch protection. This is how the 4 API column-drift CRITICALs (P-005..P-008) reached production. Renamed `ci.public.yml` workflow + concurrency group + job-display names to `CI (public-mirror)`; 4 jobs gated `if: github.repository == 'massu-ai/massu'` so the internal repo runs the FULL `ci.yml` and the public mirror keeps the public-facing subset. Job-level `name:` values PRESERVED (both rulesets reference them as required status checks). Closes P-020 (Stage A). Drift-guard: NEW `website/src/__tests__/workflow-uniqueness.test.ts` (3 assertions).
688
714
  - **NEW `website/src/app/book/opengraph-image.tsx`** — `book/layout.tsx` referenced `/og-book.png` but the file didn't exist in `website/public/`, producing a 404 image preview on every Twitter / LinkedIn / Slack share of `/book`. Replaced with the Next.js `opengraph-image.tsx` file convention — `next/og` `ImageResponse` generates a brand-consistent 1200x630 PNG at build time, Next.js auto-wires `<meta property="og:image">` and `<meta name="twitter:image">`. No static asset to maintain. Removed the broken `/og-book.png` references from `book/layout.tsx`. Closes P-021 (Stage B). Drift-guard: `website/src/__tests__/og-book-image-resolves.test.ts` (4 cases) — asserts the file exists, exports `ImageResponse` with 1200x630 dimensions, AND that any future reintroduction of `/og-book.png` in `book/layout.tsx` requires the static file to also exist.
689
715
  - **`website/src/components/sections/Hero.tsx`** — homepage had ZERO `/book` promotion above-the-fold; `/book` was only reachable via 7th nav tab. Added a pill-style `<Link href="/book">` book-announcement BEFORE the existing "Free & Source Available" badge AND before the Install Free button. `trackEvent('cta_hero_book_announcement')` for funnel measurement. Closes P-022 (Stage B). Drift-guard: `website/src/__tests__/homepage-book-cta-presence.test.ts` enforces the ordering invariant (book CTA must precede Install Free).
690
716
  - **`website/src/components/redeem/RedeemForm.tsx`** — `needs_signup` branch lost the license key, forcing customers to dig through email to re-paste the 28-char code after signup. Implemented 2-layer persistence: localStorage `massu_pending_license_key` (primary) + `?license_key=` URL fallback. Auto-fills on mount; strips URL param after hydration (no Referer/history leak); persists on `needs_signup`; clears on success; signup CTA carries `redirect_to=/redeem?license_key=…`. Closes P-023 (Stage B). Drift-guard: `__tests__/redeem-license-key-persistence.test.ts` (7 cases).
@@ -1470,7 +1496,7 @@ Auto-detect on install; zero manual config; migration via `migrateV1ToV2()`.
1470
1496
  - **`migrateV1ToV2(v1Config, detectionResult)` pure function** (`packages/core/src/detect/migrate.ts`) — lifts existing v1 configs to v2 while preserving every user override (rules, domains, canonical_paths, accessScopes, analytics, governance, security, team, conventions, etc.).
1471
1497
  - **`computeFingerprint` and `detectDrift`** (`packages/core/src/detect/drift.ts`) — SHA-256 fingerprint over normalized `DetectionResult` plus a four-axis drift report (language set, per-language framework, manifest set, workspace set).
1472
1498
  - **`verification` config block** — per-language overrides for VR-TEST, VR-TYPE, VR-BUILD, VR-SYNTAX, VR-LINT.
1473
- - **`verification_types` config block** — register custom VR-* types (e.g., `VR-IBKR-CONTRACT`, `VR-POLICY`) with descriptions.
1499
+ - **`verification_types` config block** — register custom VR-* types (e.g., `VR-LEDGER-PARITY`, `VR-POLICY`) with descriptions.
1474
1500
  - **`detection.rules` config block** — add project-specific framework signals or replace built-ins entirely with `detection.disable_builtin: true`.
1475
1501
  - **Monorepo detection** — identifies `turbo`, `nx`, `lerna`, `pnpm`, `yarn`, `bazel`, `generic`, `single`. Nested workspace support (e.g., turbo outer + pnpm inner).
1476
1502
  - **Atomic config writes** — `.tmp` file + `renameSync`; partial writes never persist. File permissions preserved on overwrite.