From 38b1a781eed63905d429feef2480c899dd203161 Mon Sep 17 00:00:00 2001 From: "Prashant Rane (ALLYIS INC)" Date: Thu, 29 Oct 2026 14:09:47 -0600 Subject: [PATCH 1/3] fix(api): qualify Hono adapter against actual Prisma parent Prepare the aged 2.1.3 override after real parent lifecycle and exploit RED/GREEN. CI-generated source locks and permanent pin regressions follow as the same bounded draft outcome. Both adapters retain a process after close; the isolated runner records and explicitly cleans up owned resources without changing parent internals or Jest worker settings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 20 ++ DEPLOYMENT.md | 2 +- README.md | 4 +- Session_starter.md | 17 ++ api/package.json | 4 +- docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md | 5 + docs/AUTHENTICATION_GUIDE.md | 2 +- docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md | 4 +- docs/COMPLETE_API_REFERENCE.md | 4 +- docs/CONTEXT_INSTRUCTIONS.md | 10 +- docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md | 2 +- docs/DOCKER_GUIDE_AND_TEST_REPORT.md | 4 +- docs/ENDPOINT_CONFIG_FLAGS_REFERENCE.md | 4 +- docs/ENDPOINT_CREATION_WIKI.md | 4 +- docs/ENDPOINT_LIFECYCLE_AND_USAGE.md | 4 +- docs/ENDPOINT_PROFILE_ARCHITECTURE.md | 2 +- .../GLOBAL_NAVIGATION_AND_WORKFLOW_CONTEXT.md | 2 +- docs/HONO_PRISMA_PARENT_QUALIFICATION.md | 182 ++++++++++++++++++ docs/INDEX.md | 8 +- docs/LOGGING_AND_OBSERVABILITY.md | 2 +- docs/MULTI_ENDPOINT_GUIDE.md | 4 +- ...ERVABILITY_TRACEABILITY_AND_DIAGNOSTICS.md | 2 +- docs/PATCH_OPERATIONS_COMPLETE_GUIDE.md | 2 +- docs/PROD_TO_DEV_MIRRORING_AND_FIXTURES.md | 4 +- docs/RELEASE_NOTES.md | 14 +- docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md | 2 +- docs/SCHEMA_ATTRIBUTE_CUSTOMIZATION_GUIDE.md | 2 +- docs/SCHEMA_CUSTOMIZATION_GUIDE.md | 2 +- docs/SOVEREIGN_AND_GOV_CLOUD_DEPLOYMENT.md | 4 +- docs/UI_GUIDE.md | 4 +- .../ENGINEERING_LESSONS_AND_PATTERNS.md | 24 +++ scripts/fixtures/prisma-hono-parent.cjs | 117 +++++++++++ scripts/qualify-prisma-hono-parent.ps1 | 86 +++++++++ web/package.json | 2 +- 34 files changed, 515 insertions(+), 40 deletions(-) create mode 100644 docs/HONO_PRISMA_PARENT_QUALIFICATION.md create mode 100644 scripts/fixtures/prisma-hono-parent.cjs create mode 100644 scripts/qualify-prisma-hono-parent.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6b17f3..4fa2ab55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### 0.55.66 preparation - Hono adapter qualified with its real Prisma parent + +- Separate draft rollback unit on exact unmerged #261 head + `f4fcfe3ccac83de6548ed9991ed44048ee8d660a`; 0.55.65 remains reserved + for untouched performance draft #251. +- Select `@hono/node-server` 2.1.3 for GHSA-rmxm-3fg6-px4f after real + Prisma 7.8.0 / dev 0.24.3 old/new startup, HTTP requests, synthetic primary + and shadow database writes, dump, public error and close/cleanup checks. + The declared exact 1.19.11 parent range remains an explicit exception. +- Actual HTTP exploit RED exposes the synthetic protected file at 1.19.15; + 2.1.3 returns 404. Six permanent focused checks have pre-manifest RED: + four failed / two passed, including the exploit and complete inventory. + GREEN, final broad counts and authenticated locks follow at consolidation. +- Both adapters retain a process after successful public close and listener + release. Explicit owned-container stop is recorded, never represented as + natural process exit or concealed by changing Jest worker/exit settings. +- [Qualification, precise limits and execution RCA](docs/HONO_PRISMA_PARENT_QUALIFICATION.md). + No new user interface or SCIM response behavior, merge, deployment, review + approval, performance clearance or release completion is claimed. + ### 0.55.64 preparation - compatible API advisory pins, stacked on draft #260 - Reserve 0.55.64 after checking existing branches, pull requests and releases. diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 9105c409..d07fc7a5 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,6 +1,6 @@ # SCIMServer Deployment Options -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` > Updated: 2026-10-16 - Scope: production + local deployment paths diff --git a/README.md b/README.md index 71b9d0ed..31103c76 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SCIMServer -> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.66` > Production-ready, multi-tenant SCIM 2.0 server for Microsoft Entra ID provisioning and any RFC 7643/7644-compliant identity client. @@ -24,7 +24,7 @@ For actual supported shapes and integration limits, see Keep strict validation enabled for Entra; diagnose the request and effective schema before applying a targeted compatibility setting. -[![Version](https://img.shields.io/badge/version-0.55.64-blue)]() +[![Version](https://img.shields.io/badge/version-0.55.66-blue)]() [![Node.js](https://img.shields.io/badge/Node.js-24-green)]() [![NestJS](https://img.shields.io/badge/NestJS-11.1-red)]() [![PostgreSQL](https://img.shields.io/badge/PostgreSQL-17-blue)]() diff --git a/Session_starter.md b/Session_starter.md index beb5abee..6410325d 100644 --- a/Session_starter.md +++ b/Session_starter.md @@ -2,6 +2,23 @@ **Last verified:** 2026-10-29 +### Separate Hono real-parent qualification (2026-10-29) + +Proposed 0.55.66 branches from exact unmerged #261 +`f4fcfe3ccac83de6548ed9991ed44048ee8d660a`, not performance #251. +[Real-parent evidence and RCA](docs/HONO_PRISMA_PARENT_QUALIFICATION.md) +records actual Prisma/dev initialization, requests, synthetic database +writes, close/port/data cleanup with old and candidate adapters. Exploit +RED/GREEN is 200 protected-file exposure versus patched 404. Both versions +retain a post-close process; only exact owned containers were stopped. +Six permanent regressions observed pre-manifest RED (four fail/two pass). +Final locks, installed GREEN, Validate and exact-tip CI are pending at source +preparation. Draft review and all release/performance/visual/prod holds remain. + +| Date | Summary | +|---|---| +| 2026-10-29 | Executed isolated actual Prisma parent old/new qualification and exploit RED/GREEN; prepared separate 0.55.66 adapter correction without changing existing drafts. | + ### Stacked API advisory remediation (2026-10-29) Proposed 0.55.64 starts from exact draft #260 head diff --git a/api/package.json b/api/package.json index 2ec131ea..f3a20680 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "scimserver-api", - "version": "0.55.64", + "version": "0.55.66", "description": "SCIMServer backend service providing SCIM 2.0 endpoints and logging for troubleshooting.", "license": "MIT", "main": "dist/main.js", @@ -81,7 +81,7 @@ }, "// overrides": "OPS-3 / Trivy gate: pin transitive deps to patched versions for known advisories. Reviewed 2026-09-30. Hono: issue #144. Multer: GHSA-wc9g-mqfw-jrwm, GHSA-qfvm-cv95-jqjf, GHSA-qvfw-j98x-7q72, GHSA-535w-7cp7-47q4. fast-uri: GHSA-5jgf-p345-68v8, GHSA-f65p-4m7j-42xc, GHSA-fph4-wmhf-6fwf, GHSA-jqff-g426-hqxp, CVE-2026-84292, CVE-2026-84394. deepmerge-ts: GHSA-ggr8-5vv4-36mx. mysql2: GHSA-3f6p-5ww8-9rcr, GHSA-rgwj-5xj2-c3m3.", "overrides": { - "@hono/node-server": "1.19.15", + "@hono/node-server": "2.1.3", "defu": "6.1.5", "effect": "3.20.0", "fast-uri": "4.2.3", diff --git a/docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md b/docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md index c1b1f795..a691238d 100644 --- a/docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md +++ b/docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md @@ -4,6 +4,11 @@ ## Executive summary +**Follow-up, separate rollback unit:** [actual Prisma parent qualification](HONO_PRISMA_PARENT_QUALIFICATION.md) +records the proposed 0.55.66 adapter correction stacked on this draft. +The 0.55.64 blocker below is preserved as the earlier evidence boundary, +not rewritten as if this draft had already qualified the adapter. + Proposed product 0.55.64 is stacked on draft PR #260 at `6eb1b59cb1a8ea324bb1a32b17355fabc605279d`. It changes only qs 6.16.0 to 6.16.1 and fast-uri 4.1.5 to 4.2.3. Neither this patch nor its parent is diff --git a/docs/AUTHENTICATION_GUIDE.md b/docs/AUTHENTICATION_GUIDE.md index aab5bc39..a9f9822e 100644 --- a/docs/AUTHENTICATION_GUIDE.md +++ b/docs/AUTHENTICATION_GUIDE.md @@ -1,6 +1,6 @@ # Authentication Guide -> **Status:** Living reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.64` +> **Status:** Living reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.66` > > **The captured protocol examples below were measured against a running server.** Request and response bodies are verbatim wire captures. Status codes and `reason_code` values are what the server actually returned. The reason-code table in [Section 8](#8-troubleshooting) is generated from [auth-reason-catalog.ts](../api/src/oauth/auth-reason-catalog.ts), so it cannot drift from the implementation. > diff --git a/docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md b/docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md index c854c113..7ce83876 100644 --- a/docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md +++ b/docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md @@ -1,8 +1,8 @@ # Azure Deployment & Usage Guide -> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** June 3, 2026 +> **Version:** 0.55.66 - **Updated:** June 3, 2026 > **Source of truth:** [deploy.ps1](../deploy.ps1), [scripts/deploy-azure.ps1](../scripts/deploy-azure.ps1), [infra/](../infra/) --- diff --git a/docs/COMPLETE_API_REFERENCE.md b/docs/COMPLETE_API_REFERENCE.md index 1b3e5f13..d2c8cb56 100644 --- a/docs/COMPLETE_API_REFERENCE.md +++ b/docs/COMPLETE_API_REFERENCE.md @@ -1,8 +1,8 @@ # Complete API Reference -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** 2026-10-16 +> **Version:** 0.55.66 - **Updated:** 2026-10-16 > **Current release:** [Release notes, upgrade steps and validation limits](RELEASE_NOTES.md). Earlier isolated P1-P9 receipts remain historical; the correctness consolidation is included in the deployed release. > **Base URL:** `http://localhost:{PORT}/scim` (configurable via `API_PREFIX` env var) > **121 route handlers** across 33 controllers (includes 2 dashboard analytics routes and the web SPA catch-all). Counted from the `@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`/`@Sse` decorators in `api/src/**/*.controller.ts` with comments stripped; the count is enforced by `node scripts/audit-doc-content.mjs`. diff --git a/docs/CONTEXT_INSTRUCTIONS.md b/docs/CONTEXT_INSTRUCTIONS.md index 8df5bec6..ee08842c 100644 --- a/docs/CONTEXT_INSTRUCTIONS.md +++ b/docs/CONTEXT_INSTRUCTIONS.md @@ -1,7 +1,7 @@ # SCIMServer - Context Instructions for AI Assistants > **Purpose**: This file provides complete project context for AI coding assistants (GitHub Copilot, etc.) to enable productive sessions without re-discovery of architecture, patterns, and decisions. -> **Version**: 0.55.64 +> **Version**: 0.55.66 > **Last Updated**: October 29, 2026 > **Last verified:** 2026-10-29 @@ -9,6 +9,14 @@ ## Active Delivery Process +**2026-10-29 actual-parent adapter follow-up:** proposed 0.55.66 is +stacked on exact #261 `f4fcfe3ccac83de6548ed9991ed44048ee8d660a`. +[Qualification and RCA](HONO_PRISMA_PARENT_QUALIFICATION.md) records real +Prisma/dev lifecycle with both adapters, security exploit RED/GREEN, and +explicit container cleanup despite inherited post-close process retention. +Existing security pins remain; #251 and reserved 0.55.65 are untouched. +Source preparation is not final lock/Validate/CI or external review proof. + **2026-10-29 stacked API advisory remediation:** proposed 0.55.64 is based on draft #260 exact head `6eb1b59cb1a8ea324bb1a32b17355fabc605279d`, not master or web #259. [Consumer compatibility and RCA](API_ADVISORY_CONSUMER_COMPATIBILITY.md) diff --git a/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md b/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md index 3faa4cb1..67005f90 100644 --- a/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md +++ b/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md @@ -9,7 +9,7 @@ or fetching them. See [minimal-schema validation](GOOGLE_MINIMAL_GROUP_MEMBERSHI [reference persistence and compatibility](GROUP_MEMBER_REFERENCE_PRESERVATION.md) and [release notes](RELEASE_NOTES.md). -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` > **P7a behavior, included since 0.55.36:** [Profile validation](SCIM_P7A_PROFILE_VALIDATION.md) > adds declaration/type/cardinality checks, required extension enforcement, diff --git a/docs/DOCKER_GUIDE_AND_TEST_REPORT.md b/docs/DOCKER_GUIDE_AND_TEST_REPORT.md index e20cb236..ca73645e 100644 --- a/docs/DOCKER_GUIDE_AND_TEST_REPORT.md +++ b/docs/DOCKER_GUIDE_AND_TEST_REPORT.md @@ -1,8 +1,8 @@ # Docker Guide -> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** June 3, 2026 +> **Version:** 0.55.66 - **Updated:** June 3, 2026 > **Source of truth:** [Dockerfile](../Dockerfile), [docker-compose.yml](../docker-compose.yml) --- diff --git a/docs/ENDPOINT_CONFIG_FLAGS_REFERENCE.md b/docs/ENDPOINT_CONFIG_FLAGS_REFERENCE.md index ae62c558..42630ef9 100644 --- a/docs/ENDPOINT_CONFIG_FLAGS_REFERENCE.md +++ b/docs/ENDPOINT_CONFIG_FLAGS_REFERENCE.md @@ -1,8 +1,8 @@ # Endpoint Configuration Flags Reference -> **Status:** User-facing reference - **Last verified:** 2026-09-29 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-09-29 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** September 29, 2026 +> **Version:** 0.55.66 - **Updated:** September 29, 2026 > **Source of truth:** [endpoint-profile.types.ts](../api/src/modules/scim/endpoint-profile/endpoint-profile.types.ts) (`ProfileSettings`) > 39 registered settings in the unreleased lifetime port: 20 boolean + 16 numeric (11 runtime-egress overrides + 3 active-credential caps + 2 token lifetimes) + 3 other typed values. `PersistRequestSecrets` is inert and `CredentialSecretVisibility` supports only `always`. See the [behavioral evidence and explicit coverage gaps](SCIM_SETTINGS_BEHAVIOR_EVIDENCE.md) and [Entra compatibility guide](SCIM_ENTRA_COMPATIBILITY.md). Registry membership is not evidence of enforcement. > 6 value types: `boolean`, `logLevel`, `primaryEnforcement`, `credentialVisibility`, `structured`, and `number` (the last added for the runtime JWKS-fetch egress knobs). diff --git a/docs/ENDPOINT_CREATION_WIKI.md b/docs/ENDPOINT_CREATION_WIKI.md index 29d7aaff..71f55690 100644 --- a/docs/ENDPOINT_CREATION_WIKI.md +++ b/docs/ENDPOINT_CREATION_WIKI.md @@ -1,8 +1,8 @@ # Create Your Own SCIM Endpoint - Self-Service Wiki -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** 2026-10-16 +> **Version:** 0.55.66 - **Updated:** 2026-10-16 > Beginner-friendly, copy-paste guide for standing up a SCIM endpoint in minutes. > **Source of truth:** [endpoint.controller.ts](../api/src/modules/endpoint/controllers/endpoint.controller.ts) - [built-in-presets.ts](../api/src/modules/scim/endpoint-profile/built-in-presets.ts) diff --git a/docs/ENDPOINT_LIFECYCLE_AND_USAGE.md b/docs/ENDPOINT_LIFECYCLE_AND_USAGE.md index 2de9b3cd..57e47c8d 100644 --- a/docs/ENDPOINT_LIFECYCLE_AND_USAGE.md +++ b/docs/ENDPOINT_LIFECYCLE_AND_USAGE.md @@ -1,8 +1,8 @@ # Endpoint Lifecycle & Usage Guide -> **Status:** User-facing reference - **Last verified:** 2026-10-12 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-12 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** June 3, 2026 +> **Version:** 0.55.66 - **Updated:** June 3, 2026 > Quick-start recipes for common SCIMServer operations --- diff --git a/docs/ENDPOINT_PROFILE_ARCHITECTURE.md b/docs/ENDPOINT_PROFILE_ARCHITECTURE.md index ae92d6fd..32813e61 100644 --- a/docs/ENDPOINT_PROFILE_ARCHITECTURE.md +++ b/docs/ENDPOINT_PROFILE_ARCHITECTURE.md @@ -1,6 +1,6 @@ # Endpoint Profile Architecture -> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.66` > **Updated:** 2026-10-17 > **Source of truth:** [endpoint-profile/](../api/src/modules/scim/endpoint-profile/) and [endpoint.service.ts](../api/src/modules/endpoint/services/endpoint.service.ts) diff --git a/docs/GLOBAL_NAVIGATION_AND_WORKFLOW_CONTEXT.md b/docs/GLOBAL_NAVIGATION_AND_WORKFLOW_CONTEXT.md index 3642fb18..6a7cd57a 100644 --- a/docs/GLOBAL_NAVIGATION_AND_WORKFLOW_CONTEXT.md +++ b/docs/GLOBAL_NAVIGATION_AND_WORKFLOW_CONTEXT.md @@ -1,6 +1,6 @@ # Global navigation and workflow context -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` See [current release notes](RELEASE_NOTES.md) for deployment and acceptance boundaries. Endpoints now uses available window height, Dashboard uses compact diff --git a/docs/HONO_PRISMA_PARENT_QUALIFICATION.md b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md new file mode 100644 index 00000000..f9d4f9b8 --- /dev/null +++ b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md @@ -0,0 +1,182 @@ +# Hono adapter: actual Prisma parent qualification + +**Last verified:** 2026-10-29 + +## Executive summary + +Proposed product 0.55.66 is a separate rollback unit stacked on unmerged +draft #261, exact base `f4fcfe3ccac83de6548ed9991ed44048ee8d660a`. +It selects `@hono/node-server` 2.1.3 only after executing the installed +Prisma parent's real lifecycle with both 1.19.15 and 2.1.3. The affected +adapter returns a synthetic protected file through the published exploit; +2.1.3 returns 404. This is bounded compatibility evidence, not acceptance +by the parent's declared dependency range. + +The public parent shutdown promise resolves and every tested listener and +stateless data file is released with both adapters. Neither version's +process naturally exits in the observed window. The runner explicitly +records that state, stops its exact owned container, verifies it stopped, +and removes it. No `forceExit`, global timer patch, parent monkeypatch, +shared database, cloud resource, or host listener is used. + +No review, merge, deployed-version change, performance qualification, +latest-image admission, or release completion is claimed. Product 0.55.65 +and performance draft #251 remain reserved and untouched. + +## What actually owns the adapter + +```text +prisma 7.8.0 + -> exact @prisma/dev 0.24.3 + -> upstream exact @hono/node-server 1.19.11 + -> project override 1.19.15 before / 2.1.3 proposed + +real startPrismaDevServer() + -> primary PGlite + shadow PGlite + local Streams + -> real Hono application + Node http.createServer + adapter serve() + -> health / authentication error / database dump HTTP requests + -> real close() + port-rebind and stateless-file assertions + -> explicit isolated-container cleanup +``` + +This flow uses the installed parent without replacing its Hono application, +database components, or adapter call with an illustrative fixture. +The adapter's `overrideGlobalObjects: false`, listening callback, Node HTTP +server creation and the parent's public `PortNotAvailableError` remain +exercised. The occupied-port test reaches the public rejection path; it does +not claim to force the narrower race inside the adapter's `EADDRINUSE` +event handler. + +## Advisory and upstream assessment + +| Question | Observed evidence | +|---|---| +| Advisory | [GHSA-rmxm-3fg6-px4f](https://github.com/advisories/GHSA-rmxm-3fg6-px4f), medium, affects `>=1.19.10 <2.1.3`. | +| Mechanism | `serveStatic` decodes a routed path again. A malformed percent-encoded path can avoid prefix authentication while still resolving a protected file inside the configured root. This is not traversal outside the root. | +| First patch | 2.1.3, published at raw UTC `2026-09-29T06:10:34Z`, over seven days old at qualification. | +| Actual fix | [Commit 9821792](https://github.com/honojs/node-server/commit/982179278529d5673812116b042829f27794966f) rejects percent signs remaining in the routed path by default, before rewriting/resolving static files. `allowPercentInPath` is an explicit opt-in. | +| Major-version cost | [v2.0.0](https://github.com/honojs/node-server/releases/tag/v2.0.0) retains the public API but rewrites request/response internals. Node >=20 and unchanged signatures alone were insufficient evidence. | +| Parent patch alternative | Prisma 7.8.0 pins dev 0.24.3 exactly. Earlier same-line dev releases retain affected 1.x requirements. Metadata for later 0.24.x releases omits the direct adapter field, which is not proof of absence from their resolved graphs. No unrelated Prisma upgrade is selected. | +| Scope of exposure | The inspected actual parent uses `serve`, not `serveStatic`. The fix is still applied to the installed affected package; no development-only or reachability suppression is introduced. | + +Existing qs, fast-uri, shared-context, and other security overrides remain. +This closes the final record in the prior six-record assessment, not every +possible advisory in the repository. + +### Worked security example + +The synthetic static server protects `/static/admin/*` with a 401 response +and then serves `/static/*`. Normal public reads still return 200. Both +`/static/admin/secret.txt` and `/static/%61dmin/secret.txt` return 401. + +| Request | Adapter 1.19.15 | Adapter 2.1.3 | +|---|---|---| +| `/static/%%36%31dmin/secret.txt` | 200, synthetic protected file exposed | 404, no protected file | + +The old-version HTTP assertion fails on `200 !== 404`. The permanent Jest +regression also fails on that precise assertion before manifest edits. +Neither test uses customer data. + +## Real parent comparison and permanent checks + +The final baseline comparison first clean-restored the exact #261 API +graph. An earlier installed performance graph had old qs/fast-uri entries; +its successful smoke is retained as preliminary evidence, not substituted +for the restored comparison. + +| Claim | Actual baseline 1.19.15 | Candidate 2.1.3 | +|---|---|---| +| Real parent initialization, HTTP readiness callback | PASS | PASS | +| `GET /health`, status/content type/exact `{ name }` body | PASS | PASS | +| Unknown route, exact 404 body | PASS | PASS | +| `POST /query` without API key, 401 `Missing API Key` | PASS | PASS | +| Primary and shadow synthetic table/insert/select | PASS | PASS | +| `POST /database/dump`, 201, only `dumpPath`, nonempty file | PASS | PASS | +| Global Fetch/Request/Response identities unchanged | PASS | PASS | +| Real close promise resolves; HTTP/primary/shadow/Streams ports rebind | PASS | PASS | +| Occupied requested port rejects with public error and exact port | PASS | PASS | +| Stateless data files removed | PASS | PASS | +| Natural process exit after close | NOT PROVEN; retained process | NOT PROVEN; same retained process | +| Owned container stopped, verified stopped and removed | PASS | PASS | + +The comparison ran Node 24.16.0 in existing `node:24-alpine`, network `none`, +no published ports, read-only dependency mounts and root filesystem, with +only a unique owned evidence directory writable. A read-only adapter-package +mount selected candidate bytes without changing any parent file. + +Permanent regressions cover the selected pin, every locked occurrence, +public resolved URL/SHA-512, real installed Prisma/dev identity and original +constraint, actual owner resolution, adapter exports/global-object contract, +the security exploit with normal-routing controls, and the complete unfiltered +installed npm inventory. The permanent lifecycle runner is separate from the +unit suite so ordinary Jest never starts an actual Prisma development server. + +```powershell +# Set DOCKER_CONFIG in this process only if the local recovery profile is needed. +pwsh -NoProfile -File scripts\qualify-prisma-hono-parent.ps1 + +# Before changing the pin, compare an extracted, approved-feed candidate: +pwsh -NoProfile -File scripts\qualify-prisma-hono-parent.ps1 ` + -ExpectedAdapter 1.19.15 +pwsh -NoProfile -File scripts\qualify-prisma-hono-parent.ps1 ` + -ExpectedAdapter 2.1.3 ` + -AdapterPath '\package' +``` + +Do not treat this as all-input compatibility. Native Windows parent +listeners, real external PostgreSQL, accelerated authenticated query-engine +downloads, concurrency/load, arbitrary streaming bodies and production +traffic are not qualified here. The test deliberately needs no network and +does not download engines or contact a cloud estate. + +## Lock provenance and consolidation + +Only the existing clean-runner `regen-lockfile.yml` may generate source +lockfiles. Import requires exact source/ref, artifact ID, authenticated archive +SHA-256, package age, public registry resolved URLs, SHA-512 entries, an +adapter-only API graph delta, and no non-root web graph delta. Approved-feed +tarball bytes must match the imported public lock integrity. Local +`package-lock-only`, feed rewriting and quarantine exceptions are forbidden. + +At source preparation the final lock import, six-test GREEN, protected +Validate push and exact-tip hosted runs are still pending. The ignored +`test-results/hono-parent-qualification-20261029/HANDOFF.md` records their +actual commands, counts and immutable receipts after consolidation. + +## Execution issues and root-cause ledger + +| Issue / severity | Symptom and mechanism | Resolution and why | Detection / earliest gate | +|---|---|---|---| +| Working-tree graph mismatch / Medium | Installed performance graph retained qs 6.16.0 and fast-uri 4.1.5 although the stacked #261 manifest requires their patches. Full npm inventory returned `ELSPROBLEMS`. | Ownership helper verified private physical directories; clean `npm ci` restored the exact #261 lock before the final old/new comparison. Same installed parent identity alone does not establish a correct complete graph. | New inventory regression / ownership plus installed-graph preflight. | +| Parent process retention / Medium | Old and new parents release ports and data after real close, but keep a process alive. Preliminary containers required explicit stop; no root cause inside the parent/runtime is claimed. | Separate happy-only and public-error-path runs showed retention in both. Permanent runner records actual pre-cleanup state, then stops/verifies/removes its exact owned container. This bounds resources without modifying the parent or pretending natural exit passed. | Actual lifecycle probe / same probe. | +| Upstream comparison lookup / Low | A fresh `v1.19.15...v2.1.3` GitHub comparison returned 404. | Read the fresh security commit and published v2.0.0/v2.1.3 release records instead; npm package metadata and verified bytes remain the identity evidence. An absent tag is not an absent fix. | Metadata intake / same intake. | +| Lockless bootstrap graph drift / High | An attempted selected-package `npm install --no-package-lock --no-save` re-resolved compatible ranges: installed Prisma became 7.10.1/dev 0.24.17 instead of locked 7.8.0/dev 0.24.3. Source locks remained byte-identical. | Discarded the privately owned wrong graph, clean-restored the exact parent manifest/lock in owned scratch state, then installed only verified candidate tarball bytes into that graph. Regenerated the client at actual 7.8.0 and complete npm inventory exited 0. Final consolidation still requires clean `npm ci` from CI-generated locks. | Post-install real-parent identity check / same check before any compatibility claim. | + +**Completeness provenance:** this ledger was reconciled with every command +and diagnosed failure in this bounded execution, including preliminary +graph smoke, RED assertions, resource retention and lookup friction. It is +not a claim to have re-audited the parent release's historical transcript. + +The bootstrap lesson is concrete: preserving a source lock does not mean +an install command used that lock. Full npm inventory validity does not +mean installed versions match the graph that was qualified. Do not repeat +lockless broad installation as a shortcut for the CI-only lock workflow. + +## Self-improvement and design disposition + +- **Applied:** a major transitive override now has real-owner identity, + actual old-byte exploit RED, complete installed inventory, and a reusable + isolated real-parent lifecycle/cleanup check. An illustrative adapter + example alone cannot satisfy that claim. +- **Accepted:** natural process exit remains unqualified and explicit. The + proposed adapter does not fix a pre-existing parent/runtime retention + behavior; no unrelated parent internals or Jest worker settings change. +- **Architecture disposition - accepted:** one dependency pin plus a small + owned-resource test runner is cohesive. No production service/controller + responsibility or coupling changes. A strategy framework would be + speculative. +- **Remaining holds:** actual external review; stack/order reconciliation + before integration with #251; historical performance failure and skipped + draft performance campaigns; operator visual verification, customer-data + and cleanup-403 holds; native/renderer and user-stopped authentication holds; + explicit customer-production approval. diff --git a/docs/INDEX.md b/docs/INDEX.md index 7920ec3e..102f5040 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -1,8 +1,8 @@ # SCIMServer Documentation Index -> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** 2026-10-21 +> **Version:** 0.55.66 - **Updated:** 2026-10-21 > 6 presets - **39 endpoint settings controls** in the unreleased lifetime port (20 boolean flags + 3 other typed settings + 16 numerics) - profile-driven User, Group, and custom ResourceType forms **Unreleased preparation:** [endpoint token lifetime integration and original @@ -11,6 +11,10 @@ current-master port from earlier isolated validation and deployed behavior. ### Start here +**Pending actual-parent adapter remediation:** [Hono and real Prisma lifecycle qualification](HONO_PRISMA_PARENT_QUALIFICATION.md) +records proposed 0.55.66, actual old/new lifecycle and exploit RED/GREEN, +explicit cleanup boundaries, and a separate draft stacked on unmerged #261. + **Pending stacked API advisory remediation:** [Actual consumer compatibility and RCA](API_ADVISORY_CONSUMER_COMPATIBILITY.md) records proposed 0.55.64, five targeted advisory records and the unchanged Hono adapter blocker. Its draft base is unmerged #260, not master. diff --git a/docs/LOGGING_AND_OBSERVABILITY.md b/docs/LOGGING_AND_OBSERVABILITY.md index 9b0b4ba9..a495246e 100644 --- a/docs/LOGGING_AND_OBSERVABILITY.md +++ b/docs/LOGGING_AND_OBSERVABILITY.md @@ -1,6 +1,6 @@ # Logging & Observability Guide -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` **Current UI presentation:** absolute timestamps in Logs, Activity, Operations and live/detail drawers include full UTC date/time and milliseconds. Copy diff --git a/docs/MULTI_ENDPOINT_GUIDE.md b/docs/MULTI_ENDPOINT_GUIDE.md index 8f68e453..d8c5699d 100644 --- a/docs/MULTI_ENDPOINT_GUIDE.md +++ b/docs/MULTI_ENDPOINT_GUIDE.md @@ -1,8 +1,8 @@ # Multi-Endpoint Architecture Guide -> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** June 3, 2026 +> **Version:** 0.55.66 - **Updated:** June 3, 2026 > **Source of truth:** [endpoint.service.ts](../api/src/modules/endpoint/services/endpoint.service.ts) --- diff --git a/docs/OBSERVABILITY_TRACEABILITY_AND_DIAGNOSTICS.md b/docs/OBSERVABILITY_TRACEABILITY_AND_DIAGNOSTICS.md index d4b36895..3509376e 100644 --- a/docs/OBSERVABILITY_TRACEABILITY_AND_DIAGNOSTICS.md +++ b/docs/OBSERVABILITY_TRACEABILITY_AND_DIAGNOSTICS.md @@ -1,6 +1,6 @@ # Observability, Traceability, Correlation IDs, Logging, Error Handling and Diagnostics -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` **Current operator presentation:** all absolute UI timestamps carry complete UTC date/time, milliseconds and timezone; invalid expiry values are explicit, diff --git a/docs/PATCH_OPERATIONS_COMPLETE_GUIDE.md b/docs/PATCH_OPERATIONS_COMPLETE_GUIDE.md index d23dcfd9..07487400 100644 --- a/docs/PATCH_OPERATIONS_COMPLETE_GUIDE.md +++ b/docs/PATCH_OPERATIONS_COMPLETE_GUIDE.md @@ -11,7 +11,7 @@ rejected later operation saves no earlier mutation. See [reference persistence and rollback compatibility](GROUP_MEMBER_REFERENCE_PRESERVATION.md) and [current release notes](RELEASE_NOTES.md). -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` > Comprehensive, source-verified reference for every PATCH option, mode, setting, path form, verb, and persistence outcome across Users, Groups, custom extensions, and custom resource types - grounded in RFC 7644 / RFC 7643 and the SCIMServer implementation. diff --git a/docs/PROD_TO_DEV_MIRRORING_AND_FIXTURES.md b/docs/PROD_TO_DEV_MIRRORING_AND_FIXTURES.md index 5e62c937..b1e86e56 100644 --- a/docs/PROD_TO_DEV_MIRRORING_AND_FIXTURES.md +++ b/docs/PROD_TO_DEV_MIRRORING_AND_FIXTURES.md @@ -1,8 +1,8 @@ # Prod -> Dev mirroring + synthetic shape-coverage fixtures -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Date:** May 6, 2026 +> **Version:** 0.55.66 - **Date:** May 6, 2026 > **Scope:** dev-tooling, no API surface change > **Affects:** [api/src/scripts/mirror-prod-to-dev.ts](../api/src/scripts/mirror-prod-to-dev.ts), [api/src/scripts/seed-shape-coverage.ts](../api/src/scripts/seed-shape-coverage.ts), [scripts/mirror-prod-to-dev.ps1](../scripts/mirror-prod-to-dev.ps1) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index b8e7620a..0b8959c8 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # SCIMServer release notes -> **Status:** User-facing reference - **Last verified:** 2026-10-23 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-23 - **Product version:** `0.55.66` These curated notes explain what changes for operators and integrators, how to upgrade, and what remains limited. The [engineering changelog](../CHANGELOG.md) @@ -15,6 +15,18 @@ notes rather than assuming that a patch label makes every rollback lossless. ## Unreleased +### Actual Prisma parent adapter preparation: 0.55.66 + +The [actual-parent qualification report](HONO_PRISMA_PARENT_QUALIFICATION.md) +records proposed `@hono/node-server` 2.1.3, stacked on unmerged draft #261. +Real old/new Prisma initialization, synthetic requests/database writes, +public shutdown and isolated cleanup precede the proposed override. +The exact upstream 1.x constraint remains a documented exception. +Natural parent-process exit is not claimed: both versions retained a process +after releasing the tested listeners and data, and the runner stopped its +owned containers explicitly. No operator migration or API contract change. +This is not a reviewed, merged, deployed or performance-qualified release. + ### Stacked API advisory preparation: 0.55.64 The [actual-consumer compatibility report](API_ADVISORY_CONSUMER_COMPATIBILITY.md) diff --git a/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md b/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md index b347938e..fc6478cc 100644 --- a/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md +++ b/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md @@ -1,6 +1,6 @@ # Remote Debugging & Diagnosis Guide -> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.66` > **Version:** 3.2 - **Source-verified against:** v0.55.36 - **Updated:** 2026-10-01 > Request-history identity and authenticated stream transport re-verified against v0.55.36 on 2026-10-01; the full line-by-line pass dates from v0.53.0. diff --git a/docs/SCHEMA_ATTRIBUTE_CUSTOMIZATION_GUIDE.md b/docs/SCHEMA_ATTRIBUTE_CUSTOMIZATION_GUIDE.md index eb755499..45f96e69 100644 --- a/docs/SCHEMA_ATTRIBUTE_CUSTOMIZATION_GUIDE.md +++ b/docs/SCHEMA_ATTRIBUTE_CUSTOMIZATION_GUIDE.md @@ -1,6 +1,6 @@ # Schema Attribute Customization Guide -> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.66` > **Version**: 1.1 - **Date**: 2026-08-04 - **Status**: Complete (PATCH merge semantics re-verified against `mergeProfilePartial()` on 2026-08-04; the full line-by-line source pass dates from v0.40.0) > **Audience**: Operators, ISV admins, Entra ID integration engineers diff --git a/docs/SCHEMA_CUSTOMIZATION_GUIDE.md b/docs/SCHEMA_CUSTOMIZATION_GUIDE.md index d88c8845..7bc9edc1 100644 --- a/docs/SCHEMA_CUSTOMIZATION_GUIDE.md +++ b/docs/SCHEMA_CUSTOMIZATION_GUIDE.md @@ -10,7 +10,7 @@ effective mutability and response projection. See [reference persistence and migration compatibility](GROUP_MEMBER_REFERENCE_PRESERVATION.md) and [release notes](RELEASE_NOTES.md). -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` > **P7a correctness, included since 0.55.36:** [Declaration and POST/PUT validation](SCIM_P7A_PROFILE_VALIDATION.md) > documents the source changes below, with owned PostgreSQL/InMemory evidence. diff --git a/docs/SOVEREIGN_AND_GOV_CLOUD_DEPLOYMENT.md b/docs/SOVEREIGN_AND_GOV_CLOUD_DEPLOYMENT.md index 10b9a654..9d9ebc8c 100644 --- a/docs/SOVEREIGN_AND_GOV_CLOUD_DEPLOYMENT.md +++ b/docs/SOVEREIGN_AND_GOV_CLOUD_DEPLOYMENT.md @@ -1,8 +1,8 @@ # SCIMServer - Sovereign & Government Cloud Deployment Guide -> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.66` -> **Version:** 0.55.64 - **Updated:** April 28, 2026 +> **Version:** 0.55.66 - **Updated:** April 28, 2026 > Covers: Azure Government (US), Azure China (21Vianet), Azure BLEU (France), Azure Germany, and custom sovereign environments --- diff --git a/docs/UI_GUIDE.md b/docs/UI_GUIDE.md index fc90e824..f232b4ff 100644 --- a/docs/UI_GUIDE.md +++ b/docs/UI_GUIDE.md @@ -1,8 +1,8 @@ # SCIMServer Web Admin UI Guide -> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.64` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.66` -> **Status:** Active | **Last Updated:** 2026-10-16 | **Version:** 0.55.64 +> **Status:** Active | **Last Updated:** 2026-10-16 | **Version:** 0.55.66 > Single-page React + Fluent UI v9 admin console. Nine pages, one shared app shell, live SSE log stream. > **Endpoint/profile/authentication flows:** [PORTABLE_ENDPOINT_PROFILE_AUTHENTICATION_AND_DISCOVERY_DESIGN.md](PORTABLE_ENDPOINT_PROFILE_AUTHENTICATION_AND_DISCOVERY_DESIGN.md) distinguishes the current Create, Discovery, Connect, endpoint Settings, and global Settings surfaces from the target profile-import workflow. > **Screenshot provenance:** every image below was re-captured on **2026-07-31** from the live **dev** estate (then `scimserver-dev.proudbush-ae90986e.eastus.azurecontainerapps.io`) running **v0.55.6 / Node v24.18.1**, at a pinned 1440x900 viewport, using: diff --git a/docs/strategy/ENGINEERING_LESSONS_AND_PATTERNS.md b/docs/strategy/ENGINEERING_LESSONS_AND_PATTERNS.md index 0d0fe63f..e57dea8b 100644 --- a/docs/strategy/ENGINEERING_LESSONS_AND_PATTERNS.md +++ b/docs/strategy/ENGINEERING_LESSONS_AND_PATTERNS.md @@ -14,6 +14,30 @@ --- +## Real-parent evidence for transitive major overrides (2026-10-29) + +**Applied in:** [Hono adapter qualification](../HONO_PRISMA_PARENT_QUALIFICATION.md). +A transitive dependency's unchanged exported function names do not prove +compatibility when its parent pins an older major and the implementation +rewrites request/response internals. Exercise the installed parent's real +initialization, requests, errors and public close contract in an owned, +network-isolated environment before changing the override. + +Keep package-level exploit RED/GREEN distinct from parent compatibility. +Verify the complete installed graph before the comparison, not just the +named parent package. Measure port/data cleanup separately from natural +process exit, and record explicit owned-resource teardown rather than +hiding a retained process with `forceExit`. Here both adapters retained a +post-close process, so natural exit remains unqualified while the public +close, listener release and stateless-file assertions passed. + +`npm install --no-package-lock --no-save ` is not a +bounded selected-package update: it can re-resolve other semver ranges. +An observed bootstrap moved Prisma 7.8.0 to 7.10.1 without touching the +source lock. Compare installed identities against the qualified lock after +every dependency operation; `npm ls --all` exiting 0 is not that comparison. +Use clean CI-generated source locks and `npm ci` for final assurance. + ## 1. The self-improvement loop (how this doc is fed and used) ### A parent-compatible dependency patch needs actual consumer evidence diff --git a/scripts/fixtures/prisma-hono-parent.cjs b/scripts/fixtures/prisma-hono-parent.cjs new file mode 100644 index 00000000..a2e57511 --- /dev/null +++ b/scripts/fixtures/prisma-hono-parent.cjs @@ -0,0 +1,117 @@ +'use strict'; +const assert = require('node:assert/strict'); +const fs = require('node:fs/promises'); +const net = require('node:net'); +const { Client } = require('pg'); +const { startPrismaDevServer, PortNotAvailableError } = require('@prisma/dev'); +const path = require('node:path'); +const packageJson = name => require(path.join(path.dirname(require.resolve(name)), '..', 'package.json')); + +async function bind(port) { + const server = net.createServer(); + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(port, '127.0.0.1', resolve); + }); + return server; +} +async function close(server) { + await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())); +} +async function main() { + const adapter = packageJson('@hono/node-server'); + assert.equal(require('prisma/package.json').version, '7.8.0'); + assert.equal(packageJson('@prisma/dev').version, '0.24.3'); + assert.equal(adapter.version, process.env.EXPECT_ADAPTER); + const name = 'scim-hono-qualification-20261029'; + const original = { Request: globalThis.Request, Response: globalThis.Response, fetch: globalThis.fetch }; + let server; + const clients = []; + try { + server = await startPrismaDevServer({ + name, persistenceMode: 'stateless', + port: 55113, databasePort: 55114, shadowDatabasePort: 55115, + }); + const streamsPort = Number(new URL(server.experimental.streams.serverUrl).port); + console.log('READY', JSON.stringify({ + adapter: adapter.version, parent: '0.24.3', prisma: '7.8.0', + http: server.http.url, experimental: Object.keys(server.experimental), + })); + for (const [key, value] of Object.entries(original)) assert.equal(globalThis[key], value); + const url = new URL(server.http.url); + url.hostname = '127.0.0.1'; + const health = await fetch(new URL('/health', url)); + assert.equal(health.status, 200); + assert.match(health.headers.get('content-type'), /application\/json/); + assert.deepEqual(await health.json(), { name }); + const missing = await fetch(new URL('/qualification-missing', url)); + assert.equal(missing.status, 404); + assert.equal(await missing.text(), '404 Not Found'); + const invalid = await fetch(new URL('/query', url), { + method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}', + }); + assert.equal(invalid.status, 401); + console.log('HTTP_ERROR', invalid.status, await invalid.text()); + for (const purpose of ['database', 'shadowDatabase']) { + const client = new Client({ connectionString: server[purpose].connectionString }); + clients.push(client); + await client.connect(); + await client.query('CREATE TABLE qualification (id integer primary key, label text not null)'); + await client.query('INSERT INTO qualification VALUES ($1, $2)', [1, 'synthetic-only']); + assert.deepEqual((await client.query('SELECT id, label FROM qualification')).rows, + [{ id: 1, label: 'synthetic-only' }]); + await client.end(); + clients.pop(); + console.log('DATABASE_OK', purpose); + } + const dump = await fetch(new URL('/database/dump', url), { method: 'POST' }); + assert.equal(dump.status, 201); + const payload = await dump.json(); + assert.deepEqual(Object.keys(payload), ['dumpPath']); + assert.equal(typeof payload.dumpPath, 'string'); + assert.ok((await fs.stat(payload.dumpPath)).size > 0); + console.log('DUMP_OK'); + await server.close(); + server = undefined; + for (const port of [55113, 55114, 55115, streamsPort]) await close(await bind(port)); + console.log('SHUTDOWN_PORTS_RELEASED'); + console.log('RESOURCES_AFTER_CLOSE', process.getActiveResourcesInfo()); + const blocker = await bind(55113); + try { + await assert.rejects(startPrismaDevServer({ + name: name + '-conflict', persistenceMode: 'stateless', + port: 55113, databasePort: 55214, shadowDatabasePort: 55215, + }), error => error instanceof PortNotAvailableError && error.port === 55113); + console.log('PORT_CONFLICT_OK'); + } finally { + await close(blocker); + } + for (const port of [55113, 55214, 55215]) await close(await bind(port)); + const dataFiles = await fs.readdir(process.env.XDG_DATA_HOME, { recursive: true, withFileTypes: true }) + .catch(error => { + if (error.code === 'ENOENT') return []; + throw error; + }); + assert.deepEqual(dataFiles.filter(entry => entry.isFile()), []); + console.log('PASS actual installed Prisma parent lifecycle', adapter.version); + console.log('RESOURCES_AFTER_ERROR', process.getActiveResourcesInfo()); + await fs.writeFile('result.json', JSON.stringify({ + passed: true, adapter: adapter.version, prisma: '7.8.0', parent: '0.24.3', + node: process.version, + claims: ['actual-parent-initialization', 'readiness', 'health-body', 'not-found-body', 'query-auth-error', + 'primary-synthetic-write', 'shadow-synthetic-write', 'nonempty-dump', + 'global-fetch-unchanged', 'http-db-shadow-stream-ports-released', + 'occupied-port-rejection', 'stateless-data-files-removed'], + naturalProcessExit: 'not asserted; runner records actual container state before cleanup', + }, null, 2) + '\n'); + } finally { + for (const client of clients) await client.end(); + if (server) await server.close(); + } +} +main().catch(error => { console.error(error); process.exitCode = 1; }).finally(() => { + setTimeout(() => { + console.log('DELAYED_RESOURCES', process.getActiveResourcesInfo()); + console.log('REFERENCED_LIBUV', JSON.stringify(process.report.getReport().libuv.filter(h => h.is_referenced))); + }, 1500).unref(); +}); diff --git a/scripts/qualify-prisma-hono-parent.ps1 b/scripts/qualify-prisma-hono-parent.ps1 new file mode 100644 index 00000000..bfdf49df --- /dev/null +++ b/scripts/qualify-prisma-hono-parent.ps1 @@ -0,0 +1,86 @@ +[CmdletBinding()] +param( + [string]$ExpectedAdapter = '2.1.3', + [string]$AdapterPath, + [ValidateRange(30, 180)] + [int]$TimeoutSeconds = 90 +) + +$ErrorActionPreference = 'Stop' +$root = Split-Path $PSScriptRoot -Parent +& (Join-Path $PSScriptRoot 'assert-dependency-ownership.ps1') -RepositoryRoot $root +if ($LASTEXITCODE) { throw 'Dependency ownership preflight failed.' } + +$run = [Guid]::NewGuid().ToString('N') +$name = "scim-hono-qualification-$run" +$state = Join-Path $root "test-results\hono-parent-qualification-20261029\run-$run" +New-Item -ItemType Directory -Path $state | Out-Null +$args = @( + 'create', '--name', $name, '--network', 'none', '--read-only', + '--label', "com.scimserver.hono-qualification=$run", + '--mount', "type=bind,source=$root\api\node_modules,target=/qualification/node_modules,readonly", + '--mount', "type=bind,source=$PSScriptRoot\fixtures,target=/qualification/fixtures,readonly", + '--mount', "type=bind,source=$state,target=/qualification/state", + '--workdir', '/qualification/state', + '--env', 'HOME=/qualification/state/home', + '--env', 'XDG_DATA_HOME=/qualification/state/data', + '--env', 'XDG_CONFIG_HOME=/qualification/state/config', + '--env', 'XDG_CACHE_HOME=/qualification/state/cache', + '--env', "EXPECT_ADAPTER=$ExpectedAdapter" +) +if ($AdapterPath) { + $adapter = (Resolve-Path -LiteralPath $AdapterPath).Path + $qualificationRoot = Join-Path $root 'test-results\hono-parent-qualification-20261029\' + if (-not $adapter.StartsWith($qualificationRoot, [StringComparison]::OrdinalIgnoreCase)) { + throw 'Candidate adapter must be an extracted package inside the owned qualification evidence directory.' + } + $args += @('--mount', "type=bind,source=$adapter,target=/qualification/node_modules/@hono/node-server,readonly") +} +$args += @('node:24-alpine', 'node', '/qualification/fixtures/prisma-hono-parent.cjs') +$container = $null +try { + $container = (& docker @args | Out-String).Trim() + if ($LASTEXITCODE -ne 0 -or $container -notmatch '^[a-f0-9]{64}$') { + throw 'Could not create the isolated qualification container.' + } + $inspection = & docker inspect $container | ConvertFrom-Json + if ($LASTEXITCODE -ne 0 -or $inspection[0].Name -ne "/$name" -or + $inspection[0].Config.Labels.'com.scimserver.hono-qualification' -ne $run -or + $inspection[0].HostConfig.NetworkMode -ne 'none' -or + $inspection[0].HostConfig.PortBindings.PSObject.Properties.Count -gt 0) { + throw 'Container ownership or network isolation verification failed.' + } + $inspection | ConvertTo-Json -Depth 30 | Set-Content (Join-Path $state 'container.json') + & docker start $container | Out-Null + if ($LASTEXITCODE -ne 0) { throw 'Container startup failed.' } + $deadline = [DateTimeOffset]::UtcNow.AddSeconds($TimeoutSeconds) + $resultPath = Join-Path $state 'result.json' + while (-not (Test-Path -LiteralPath $resultPath)) { + if ([DateTimeOffset]::UtcNow -gt $deadline) { throw "Parent qualification exceeded $TimeoutSeconds seconds." } + $status = & docker inspect --format '{{.State.Running}}' $container + if ($LASTEXITCODE -ne 0 -or $status -ne 'true') { throw 'Parent exited before completing its lifecycle assertions.' } + Start-Sleep -Seconds 1 + } + $result = Get-Content -Raw -LiteralPath $resultPath | ConvertFrom-Json + if ($result.passed -ne $true -or $result.adapter -ne $ExpectedAdapter -or $result.claims.Count -ne 12) { + throw 'Incomplete or mismatched parent qualification result.' + } + Start-Sleep -Seconds 2 + & docker inspect --format '{{json .State}}' $container | + Set-Content (Join-Path $state 'state-before-cleanup.json') + Write-Host "PASS: actual parent lifecycle assertions completed. Evidence: $state" + Write-Host 'Natural process exit is not asserted. Any retained process is explicitly stopped below.' +} finally { + if ($container -match '^[a-f0-9]{64}$') { + & docker logs $container *> (Join-Path $state 'parent.log') + & docker stop --timeout 5 $container | Out-Null + if ($LASTEXITCODE -ne 0) { throw "Could not stop owned container $container." } + $stopped = & docker inspect --format '{{.State.Running}}' $container + if ($LASTEXITCODE -ne 0 -or $stopped -ne 'false') { throw "Owned container $container is still running." } + & docker inspect --format '{{json .State}}' $container | + Set-Content (Join-Path $state 'state-after-cleanup.json') + & docker rm $container | Out-Null + if ($LASTEXITCODE -ne 0) { throw "Could not remove owned container $container." } + Write-Host "CLEANUP: stopped and removed exact owned container $container." + } +} diff --git a/web/package.json b/web/package.json index 3973518d..369151a8 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,7 @@ { "name": "scimserver-web", "private": true, - "version": "0.55.64", + "version": "0.55.66", "type": "module", "scripts": { "dev": "vite", From abb9568dd0b84f3720a36e337f818a3033203f37 Mon Sep 17 00:00:00 2001 From: "Prashant Rane (ALLYIS INC)" Date: Thu, 29 Oct 2026 14:29:58 -0600 Subject: [PATCH 2/3] test(api): consolidate qualified Hono pin with authenticated locks Import exact-source CI lock artifacts, enforce the complete installed parent graph, and preserve the isolated real-parent lifecycle proof and cleanup boundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 10 +- Session_starter.md | 9 +- api/package-lock.json | 12 +- .../check-hono-parent-qualification.spec.ts | 121 ++++++++++++++++++ docs/CONTEXT_INSTRUCTIONS.md | 7 +- docs/HONO_PRISMA_PARENT_QUALIFICATION.md | 27 +++- scripts/fixtures/prisma-hono-parent.cjs | 5 + scripts/qualify-prisma-hono-parent.ps1 | 1 + web/package-lock.json | 4 +- 9 files changed, 182 insertions(+), 14 deletions(-) create mode 100644 api/src/scripts/check-hono-parent-qualification.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fa2ab55..9611cf3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Actual HTTP exploit RED exposes the synthetic protected file at 1.19.15; 2.1.3 returns 404. Six permanent focused checks have pre-manifest RED: four failed / two passed, including the exploit and complete inventory. - GREEN, final broad counts and authenticated locks follow at consolidation. + Final focused GREEN is 6/6 in one suite, targeted lint passes, and full + unfiltered installed npm inventory exits 0 with empty stderr. +- Exact-source CI lock run 40948177653 and authenticated artifact 12929363808 + change only the API adapter outside root records; both clean restores + preserve their source lock bytes. Final clean-installed actual-parent + isolation check passes all 12 claims and removes its owned container. +- Bootstrap protected Fast passes 25/25, with 6,248 API unit and 2,514 + HTTP E2E tests (42 existing skipped). Final Validate/hosted counts are + recorded separately in the bounded handoff after execution, not assumed. - Both adapters retain a process after successful public close and listener release. Explicit owned-container stop is recorded, never represented as natural process exit or concealed by changing Jest worker/exit settings. diff --git a/Session_starter.md b/Session_starter.md index 6410325d..34d792a0 100644 --- a/Session_starter.md +++ b/Session_starter.md @@ -12,8 +12,13 @@ writes, close/port/data cleanup with old and candidate adapters. Exploit RED/GREEN is 200 protected-file exposure versus patched 404. Both versions retain a post-close process; only exact owned containers were stopped. Six permanent regressions observed pre-manifest RED (four fail/two pass). -Final locks, installed GREEN, Validate and exact-tip CI are pending at source -preparation. Draft review and all release/performance/visual/prod holds remain. +Authenticated CI lock run 40948177653 was imported with an adapter-only API +delta and no non-root web delta. Both clean restores preserve lock bytes. +Installed focused GREEN is 6/6; full npm inventory exits 0 with empty stderr. +Final installed parent passes all 12 claims; native invocation refuses before +loading the parent. Bootstrap Fast passes 25/25; final Validate/exact-tip CI +receipts follow separately in the ignored handoff. Draft review and all +release/performance/visual/prod holds remain. | Date | Summary | |---|---| diff --git a/api/package-lock.json b/api/package-lock.json index 8efa1d0f..921bb0d7 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,12 +1,12 @@ { "name": "scimserver-api", - "version": "0.55.64", + "version": "0.55.66", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scimserver-api", - "version": "0.55.64", + "version": "0.55.66", "license": "MIT", "dependencies": { "@nestjs/common": "^11.1.27", @@ -765,13 +765,13 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.15", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.15.tgz", - "integrity": "sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.3.tgz", + "integrity": "sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==", "devOptional": true, "license": "MIT", "engines": { - "node": ">=18.14.1" + "node": ">=20" }, "peerDependencies": { "hono": "^4" diff --git a/api/src/scripts/check-hono-parent-qualification.spec.ts b/api/src/scripts/check-hono-parent-qualification.spec.ts new file mode 100644 index 00000000..eaa352d8 --- /dev/null +++ b/api/src/scripts/check-hono-parent-qualification.spec.ts @@ -0,0 +1,121 @@ +import { spawnSync } from 'node:child_process'; +import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { satisfiesGhsaRange } from './check-dependency-pins'; + +type Package = { + name?: string; + version: string; + resolved?: string; + integrity?: string; + dependencies?: Record; +}; +const apiRoot = resolve(__dirname, '../..'); +const readPackage = (path: string): Package => JSON.parse(readFileSync(path, 'utf8')) as Package; +const installedPackage = (name: string, owner = apiRoot): Package => + readPackage(resolve(dirname(require.resolve(name, { paths: [owner] })), '..', 'package.json')); +const lock = JSON.parse(readFileSync(resolve(apiRoot, 'package-lock.json'), 'utf8')) as { + packages: Record; +}; +const manifest = JSON.parse(readFileSync(resolve(apiRoot, 'package.json'), 'utf8')) as { + overrides: Record; +}; +const range = '>= 1.19.10, < 2.1.3'; + +describe('Hono adapter qualification for its actual Prisma parent', () => { + it('distinguishes the affected baseline from the first fixed release', () => { + expect(satisfiesGhsaRange('1.19.15', range)).toBe(true); + expect(satisfiesGhsaRange('2.1.3', range)).toBe(false); + }); + + it('pins every locked occurrence to the qualified public package', () => { + expect(manifest.overrides['@hono/node-server']).toBe('2.1.3'); + const occurrences = Object.entries(lock.packages).filter(([path]) => + path.endsWith('node_modules/@hono/node-server'), + ); + expect(occurrences.map(([path]) => path)).toEqual(['node_modules/@hono/node-server']); + for (const [, entry] of occurrences) { + expect(entry.version).toBe('2.1.3'); + expect(satisfiesGhsaRange(entry.version, range)).toBe(false); + expect(entry.resolved).toBe('https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.3.tgz'); + expect(entry.integrity).toMatch(/^sha512-[A-Za-z0-9+/]+=*$/); + } + }); + + it('keeps the actual Prisma owner and its upstream adapter constraint explicit', () => { + const prismaPath = dirname(require.resolve('prisma/package.json')); + const prisma = readPackage(resolve(prismaPath, 'package.json')); + const devEntry = require.resolve('@prisma/dev', { paths: [prismaPath] }); + const dev = installedPackage('@prisma/dev', prismaPath); + expect(prisma.version).toBe('7.8.0'); + expect(prisma.dependencies?.['@prisma/dev']).toBe('0.24.3'); + expect(dev.name).toBe('@prisma/dev'); + expect(dev.version).toBe('0.24.3'); + expect(dev.dependencies?.['@hono/node-server']).toBe('1.19.11'); + expect(installedPackage('@hono/node-server', dirname(devEntry)).version).toBe('2.1.3'); + expect(lock.packages['node_modules/prisma'].version).toBe(prisma.version); + expect(lock.packages['node_modules/@prisma/dev'].version).toBe(dev.version); + }); + + it('retains the parent-facing adapter exports without opening a listener', () => { + const adapter = require('@hono/node-server') as { + serve: unknown; + getRequestListener: unknown; + createAdaptorServer: (options: { fetch: () => Response; overrideGlobalObjects: boolean }) => { + listening: boolean; + }; + }; + const request = globalThis.Request; + const response = globalThis.Response; + expect(typeof adapter.serve).toBe('function'); + expect(typeof adapter.getRequestListener).toBe('function'); + const server = adapter.createAdaptorServer({ + fetch: () => new Response('synthetic'), + overrideGlobalObjects: false, + }); + expect(server.listening).toBe(false); + expect(globalThis.Request).toBe(request); + expect(globalThis.Response).toBe(response); + }); + + it('rejects the real double-decode exploit while retaining normal routing', async () => { + const { Hono } = require('hono') as typeof import('hono'); + const { serveStatic } = require('@hono/node-server/serve-static') as + typeof import('@hono/node-server/serve-static'); + const root = resolve(apiRoot, '../test-results', `hono-static-regression-${process.pid}`); + mkdirSync(resolve(root, 'static/admin'), { recursive: true }); + writeFileSync(resolve(root, 'static/admin/secret.txt'), 'synthetic-protected'); + writeFileSync(resolve(root, 'static/public.txt'), 'synthetic-public'); + try { + const app = new Hono(); + app.use('/static/admin/*', c => c.text('Unauthorized', 401)); + app.use('/static/*', serveStatic({ root })); + const publicFile = await app.request('/static/public.txt'); + expect(publicFile.status).toBe(200); + expect(await publicFile.text()).toBe('synthetic-public'); + for (const path of ['/static/admin/secret.txt', '/static/%61dmin/secret.txt']) { + const protectedFile = await app.request(path); + expect(protectedFile.status).toBe(401); + expect(await protectedFile.text()).toBe('Unauthorized'); + } + const exploit = await app.request('/static/%%36%31dmin/secret.txt'); + expect(exploit.status).toBe(404); + expect(await exploit.text()).not.toBe('synthetic-protected'); + } finally { + rmSync(root, { recursive: true, force: true }); + } + }); + + it('requires the complete installed npm inventory to be valid', () => { + const npm = process.env.npm_execpath; + expect(npm).toBeDefined(); + const inventory = spawnSync(process.execPath, [npm!, 'ls', '--all', '--json'], { + cwd: apiRoot, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024, + }); + expect(inventory.error).toBeUndefined(); + expect({ status: inventory.status, error: inventory.stderr.slice(0, 1000) }) + .toEqual({ status: 0, error: '' }); + const tree = JSON.parse(inventory.stdout) as { problems?: string[] }; + expect(tree.problems ?? []).toEqual([]); + }); +}); diff --git a/docs/CONTEXT_INSTRUCTIONS.md b/docs/CONTEXT_INSTRUCTIONS.md index ee08842c..e8adef35 100644 --- a/docs/CONTEXT_INSTRUCTIONS.md +++ b/docs/CONTEXT_INSTRUCTIONS.md @@ -15,7 +15,12 @@ stacked on exact #261 `f4fcfe3ccac83de6548ed9991ed44048ee8d660a`. Prisma/dev lifecycle with both adapters, security exploit RED/GREEN, and explicit container cleanup despite inherited post-close process retention. Existing security pins remain; #251 and reserved 0.55.65 are untouched. -Source preparation is not final lock/Validate/CI or external review proof. +Authenticated CI lock run 40948177653 changes only the API adapter outside +root records. Clean restores retain lock bytes; installed focused GREEN is +6/6 and complete npm inventory exits 0. Final real-parent isolated run passes +all 12 claims and cleans up its container; native invocation fails closed. +Final protected Validate/exact-tip CI remain separate receipts in the ignored +handoff. No external review or overall release proof is inferred. **2026-10-29 stacked API advisory remediation:** proposed 0.55.64 is based on draft #260 exact head `6eb1b59cb1a8ea324bb1a32b17355fabc605279d`, diff --git a/docs/HONO_PRISMA_PARENT_QUALIFICATION.md b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md index f9d4f9b8..02bea1de 100644 --- a/docs/HONO_PRISMA_PARENT_QUALIFICATION.md +++ b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md @@ -110,6 +110,11 @@ constraint, actual owner resolution, adapter exports/global-object contract, the security exploit with normal-routing controls, and the complete unfiltered installed npm inventory. The permanent lifecycle runner is separate from the unit suite so ordinary Jest never starts an actual Prisma development server. +The fixture refuses native invocation before loading its parent. It requires +the runner's explicit flag, a Linux container marker, and internal-only network +interfaces. The final clean-installed run passed all 12 lifecycle claims and +stopped/removed its exact owned container; the native refusal check failed +closed as expected. ```powershell # Set DOCKER_CONFIG in this process only if the local recovery profile is needed. @@ -138,8 +143,24 @@ adapter-only API graph delta, and no non-root web graph delta. Approved-feed tarball bytes must match the imported public lock integrity. Local `package-lock-only`, feed rewriting and quarantine exceptions are forbidden. -At source preparation the final lock import, six-test GREEN, protected -Validate push and exact-tip hosted runs are still pending. The ignored +Clean-runner workflow `40948177653`, exact source +`38b1a781eed63905d429feef2480c899dd203161`, produced artifact `12929363808`. +The authenticated archive SHA-256 is +`bf1c5791b9cfddf1a9e22beacd71a4549835c3d37ebf32db413e0d4629048946`. +The API lock changes only the adapter outside its root record; the web lock +changes no non-root record. Both clean installs retained byte-identical locks, +Prisma client generation reported 7.8.0, and approved-feed adapter bytes match +the imported public SHA-512. The public publication timestamp remains the raw +UTC string `2026-09-29T06:10:34.512Z`. + +The final permanent suite passes 6/6 in one suite, targeted ESLint passes, +and the complete unfiltered installed API npm inventory exits 0 with empty +stderr. Bootstrap protected Fast passed 25/25 gates, including 6,248 API +unit tests in 215 suites and 2,514 HTTP E2E tests (42 existing skipped). +Those bootstrap counts do not include the six permanent tests restored at +lock consolidation. Final protected Validate and exact-tip hosted runs are +separate pending publication evidence, not inferred from that earlier run. +The ignored `test-results/hono-parent-qualification-20261029/HANDOFF.md` records their actual commands, counts and immutable receipts after consolidation. @@ -151,6 +172,8 @@ actual commands, counts and immutable receipts after consolidation. | Parent process retention / Medium | Old and new parents release ports and data after real close, but keep a process alive. Preliminary containers required explicit stop; no root cause inside the parent/runtime is claimed. | Separate happy-only and public-error-path runs showed retention in both. Permanent runner records actual pre-cleanup state, then stops/verifies/removes its exact owned container. This bounds resources without modifying the parent or pretending natural exit passed. | Actual lifecycle probe / same probe. | | Upstream comparison lookup / Low | A fresh `v1.19.15...v2.1.3` GitHub comparison returned 404. | Read the fresh security commit and published v2.0.0/v2.1.3 release records instead; npm package metadata and verified bytes remain the identity evidence. An absent tag is not an absent fix. | Metadata intake / same intake. | | Lockless bootstrap graph drift / High | An attempted selected-package `npm install --no-package-lock --no-save` re-resolved compatible ranges: installed Prisma became 7.10.1/dev 0.24.17 instead of locked 7.8.0/dev 0.24.3. Source locks remained byte-identical. | Discarded the privately owned wrong graph, clean-restored the exact parent manifest/lock in owned scratch state, then installed only verified candidate tarball bytes into that graph. Regenerated the client at actual 7.8.0 and complete npm inventory exited 0. Final consolidation still requires clean `npm ci` from CI-generated locks. | Post-install real-parent identity check / same check before any compatibility claim. | +| Transitional source CI / Low | Bootstrap image run `40948154638` rejected the deliberately stale lock: 1.19.15 does not satisfy 2.1.3. The source must first be published for the existing CI-only lock workflow. | Exact-source regeneration run `40948177653` succeeded; imported its authenticated minimal artifact and clean-restored both workspaces. This is a transitional failed run, not final-tip success or a waived install gate. | Hosted install / source-lock consistency check; expected until artifact import. | +| Evidence replay baseline / Low | Rerunning the pre-import lock comparison after import reported zero changed records, correctly failing its expected one-record delta. Its mutable baseline was now the imported artifact itself. | Pinned the comparison to bootstrap commit `38b1a78` and separately require current source bytes to equal the authenticated artifact. Baseline identity and import identity are now independent assertions. | Local evidence replay / immutable-baseline check before reuse. | **Completeness provenance:** this ledger was reconciled with every command and diagnosed failure in this bounded execution, including preliminary diff --git a/scripts/fixtures/prisma-hono-parent.cjs b/scripts/fixtures/prisma-hono-parent.cjs index a2e57511..6edcc088 100644 --- a/scripts/fixtures/prisma-hono-parent.cjs +++ b/scripts/fixtures/prisma-hono-parent.cjs @@ -2,6 +2,11 @@ const assert = require('node:assert/strict'); const fs = require('node:fs/promises'); const net = require('node:net'); +if (process.env.SCIM_HONO_ISOLATED !== '1' || process.platform !== 'linux' || + !require('node:fs').existsSync('/.dockerenv') || + Object.values(require('node:os').networkInterfaces()).flat().some(address => !address.internal)) { + throw new Error('Actual parent qualification requires the network-isolated container runner.'); +} const { Client } = require('pg'); const { startPrismaDevServer, PortNotAvailableError } = require('@prisma/dev'); const path = require('node:path'); diff --git a/scripts/qualify-prisma-hono-parent.ps1 b/scripts/qualify-prisma-hono-parent.ps1 index bfdf49df..607f8798 100644 --- a/scripts/qualify-prisma-hono-parent.ps1 +++ b/scripts/qualify-prisma-hono-parent.ps1 @@ -26,6 +26,7 @@ $args = @( '--env', 'XDG_DATA_HOME=/qualification/state/data', '--env', 'XDG_CONFIG_HOME=/qualification/state/config', '--env', 'XDG_CACHE_HOME=/qualification/state/cache', + '--env', 'SCIM_HONO_ISOLATED=1', '--env', "EXPECT_ADAPTER=$ExpectedAdapter" ) if ($AdapterPath) { diff --git a/web/package-lock.json b/web/package-lock.json index 20bf8a4b..513a5a0a 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "scimserver-web", - "version": "0.55.64", + "version": "0.55.66", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scimserver-web", - "version": "0.55.64", + "version": "0.55.66", "dependencies": { "@fluentui/react-components": "^9.74.2", "@fluentui/react-icons": "^2.0.331", From 7379a97e3f64cb054fac2a5ff34cdbe144027993 Mon Sep 17 00:00:00 2001 From: "Prashant Rane (ALLYIS INC)" Date: Thu, 29 Oct 2026 14:40:55 -0600 Subject: [PATCH 3/3] docs: record process-local Prisma validation environment recovery Record the protected push failure and confirmed focused generate recovery without changing any gate, database or production source behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/HONO_PRISMA_PARENT_QUALIFICATION.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/HONO_PRISMA_PARENT_QUALIFICATION.md b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md index 02bea1de..408a1fe4 100644 --- a/docs/HONO_PRISMA_PARENT_QUALIFICATION.md +++ b/docs/HONO_PRISMA_PARENT_QUALIFICATION.md @@ -174,6 +174,7 @@ actual commands, counts and immutable receipts after consolidation. | Lockless bootstrap graph drift / High | An attempted selected-package `npm install --no-package-lock --no-save` re-resolved compatible ranges: installed Prisma became 7.10.1/dev 0.24.17 instead of locked 7.8.0/dev 0.24.3. Source locks remained byte-identical. | Discarded the privately owned wrong graph, clean-restored the exact parent manifest/lock in owned scratch state, then installed only verified candidate tarball bytes into that graph. Regenerated the client at actual 7.8.0 and complete npm inventory exited 0. Final consolidation still requires clean `npm ci` from CI-generated locks. | Post-install real-parent identity check / same check before any compatibility claim. | | Transitional source CI / Low | Bootstrap image run `40948154638` rejected the deliberately stale lock: 1.19.15 does not satisfy 2.1.3. The source must first be published for the existing CI-only lock workflow. | Exact-source regeneration run `40948177653` succeeded; imported its authenticated minimal artifact and clean-restored both workspaces. This is a transitional failed run, not final-tip success or a waived install gate. | Hosted install / source-lock consistency check; expected until artifact import. | | Evidence replay baseline / Low | Rerunning the pre-import lock comparison after import reported zero changed records, correctly failing its expected one-record delta. Its mutable baseline was now the imported artifact itself. | Pinned the comparison to bootstrap commit `38b1a78` and separately require current source bytes to equal the authenticated artifact. Baseline identity and import identity are now independent assertions. | Local evidence replay / immutable-baseline check before reuse. | +| Process-local validation environment / Low | The first protected Validate passed 6,254 unit and 2,514 E2E tests, then Prisma generation rejected a missing `DATABASE_URL`. The earlier generation shell's process-local value did not persist into the push shell. | The unchanged generate command passed after deriving the exact checked-in `build-test.yml` CI value without printing it. Repeat that derivation in the same process as the normal Validate push; no database connection, global configuration or gate change is required. | Protected generate gate / process-environment preflight before push. | **Completeness provenance:** this ledger was reconciled with every command and diagnosed failure in this bounded execution, including preliminary @@ -191,6 +192,9 @@ lockless broad installation as a shortcut for the CI-only lock workflow. actual old-byte exploit RED, complete installed inventory, and a reusable isolated real-parent lifecycle/cleanup check. An illustrative adapter example alone cannot satisfy that claim. +- **Applied:** validation process setup must be repeated in the push shell. + A successful earlier generation process is not evidence that a later shell + inherited its environment. - **Accepted:** natural process exit remains unqualified and explicit. The proposed adapter does not fix a pre-existing parent/runtime retention behavior; no unrelated parent internals or Jest worker settings change.