diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a422618..1e4bc1d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### 0.55.62 web advisory patch bootstrap (2026-10-29) + +- Reserve 0.55.62 from merged master `727860866c53c7ec8d01d1ff5bae858f80a1715d`. + Change only exact web Seroval 1.6.3 -> 1.6.11 and Undici 7.29.1 -> 7.30.1; + preserve the API dependency graph and merged security/fixture corrections. +- Focused TDD RED: six real assertions fail before manifest edits across pins, + all lock occurrences and all installed owners. Preserve the draft outside + source until authenticated CI lock import; bootstrap hook GREEN is old-tree + evidence only, not a patched dependency result. +- Prior merged counts: API units 6,220; HTTP 2,514 plus 42 skips; web 1,655. + Final current-tip counts and CI-owned lock provenance remain pending. +- Authenticated regeneration 40895972413 / artifact 12906712562 changes only + the two web packages and coordinated release metadata. Clean restores keep + both CI lock hashes unchanged. Focused GREEN: 35/35, including six new + regressions. Web full npm inventory exits 0; existing API full-tree owner + context errors remain a separate explicit limitation, not blanket clearance. +- Bootstrap ordinary Fast hook: 25/25 gates, 618.2 seconds; units 6,220 and + HTTP 2,514 plus 42 skips. The retained native/OS HTTP worker warning is not + reclassified as a fixed unit-fixture leak. +- Hosted CI caught a local-only prerequisite assumption: API units run + before web installation. Move all six dependency regressions to the + existing web Vitest lane, preserving every assertion and using native + owner resolution. Focused web GREEN: 6/6. No workflow/gate redesign or skip. + Earlier local Validate at `52200e33` passed 28/28 in 783.1 seconds with + units 6,226, HTTP 2,514 +42 skips and web 1,655; those are not final-tip + proof after the test relocation. Fresh protected Validate is required. +- Update user-facing release notes, indexed scope/RCA, Session and Context. + Issue #256 stays open for six independent API advisory records. +- Self-improvement: all-occurrence and real-owner regressions, without skips. + Design disposition: accepted, existing exact override/process is cohesive. + No application refactor, browser behavior change, merge or deployment. + ### Fixture branch integration with merged security master (2026-10-29) - Normally integrate security PR #258 merge `80460d59` into draft PR #257, diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index a622f594e..f1a5cd9ad 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` > Updated: 2026-10-16 - Scope: production + local deployment paths diff --git a/README.md b/README.md index d459b1024..628582568 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.62` > Production-ready, multi-tenant SCIM 2.0 server for Microsoft Entra ID provisioning and any RFC 7643/7644-compliant identity client. @@ -15,7 +15,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.61-blue)]() +[![Version](https://img.shields.io/badge/version-0.55.62-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 35d821e09..43bfdb928 100644 --- a/Session_starter.md +++ b/Session_starter.md @@ -2,6 +2,27 @@ **Last verified:** 2026-10-29 +### Independent web advisory patch boundary (2026-10-29) + +PR #257 is normally merged at `727860866c53c7ec8d01d1ff5bae858f80a1715d`. +The independent branch `fix/web-seroval-undici-20261029` reserves 0.55.62: +web Seroval 1.6.11 and Undici 7.30.1 only, API graph unchanged. +Six focused assertions failed on actual old pins/locks/installed owners before +manifest edits. CI-only locks are authenticated from run 40895972413 and +artifact 12906712562; all 35 focused tests and full web npm inventory pass. +API package graph is unchanged; its pre-existing full-tree override-context +errors remain explicit, despite top-level inventory exit 0. +Final protected Validate and hosted review remain pending. +Historical `52200e33` Validate passed locally, but hosted CI correctly failed +installed-web tests in the API-before-web-install lane. All six regressions +now live in web's existing post-install Vitest lane and pass focused GREEN. +No workflow order, test skip or assertion relaxation was introduced. +See [scope, RCA and validation](docs/WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md). +The prior fixture/draft boundary below is historical. Keep issue #256 open, +PR #251 untouched, and all release holds. No merge or deployment authorized. +Self-improvement is applied through owner/all-occurrence checks; design +disposition is accepted because existing exact overrides need no abstraction. + ### Current consolidation boundary (2026-10-29) Security PR #258 is normally merged into master at `80460d59`. Draft PR #257 diff --git a/api/package-lock.json b/api/package-lock.json index efed6b6c0..51a8aeb96 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,12 +1,12 @@ { "name": "scimserver-api", - "version": "0.55.61", + "version": "0.55.62", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scimserver-api", - "version": "0.55.61", + "version": "0.55.62", "license": "MIT", "dependencies": { "@nestjs/common": "^11.1.27", diff --git a/api/package.json b/api/package.json index 7bfac3bcd..9445cccdf 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "scimserver-api", - "version": "0.55.61", + "version": "0.55.62", "description": "SCIMServer backend service providing SCIM 2.0 endpoints and logging for troubleshooting.", "license": "MIT", "main": "dist/main.js", diff --git a/docs/AUTHENTICATION_GUIDE.md b/docs/AUTHENTICATION_GUIDE.md index b30413c01..fa9e0fe90 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.61` +> **Status:** Living reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.62` > > **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 35b0d6c3f..6aecdae20 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** June 3, 2026 +> **Version:** 0.55.62 - **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 7fa5cccea..52abbd5c7 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** 2026-10-16 +> **Version:** 0.55.62 - **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 01ad0569a..a24b657bd 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.61 +> **Version**: 0.55.62 > **Last Updated**: October 29, 2026 > **Last verified:** 2026-10-29 @@ -9,6 +9,22 @@ ## Active Delivery Process +**2026-10-29 independent web advisory patch:** merged master is +`727860866c53c7ec8d01d1ff5bae858f80a1715d` (0.55.61). +The new bounded patch reserves 0.55.62 for Seroval 1.6.11 and Undici 7.30.1, +with no API dependency graph changes. Six genuine focused RED assertions are +retained; authenticated CI locks (run 40895972413), 35/35 focused tests and +actual full web npm inventory now pass. API graph is unchanged, with existing +full-tree override-context errors explicitly retained as a separate finding. +Normal final Validate and hosted review remain pending. +Hosted source `52200e33` exposed API tests reading web packages before CI +installs them. All six new regressions now run in web's existing Vitest lane; +focused 6/6 passes without workflow changes or skips. Prior local Validate +results are historical and must not be substituted for the corrected tip. +See [scope and RCA](WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md). +This supersedes the older draft #257 status below. No merge/deployment, +release admission or closure of issue #256 is authorized. + **2026-10-29 current consolidation:** security PR #258 merged normally at `80460d59`. Draft #257 integrates that master while preserving its prior `9d6d5c62` fixture/diagnosis history. Product remains 0.55.61 and CI-owned diff --git a/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md b/docs/CUSTOM_EXTENSIONS_RFC_GUIDE.md index b790996ab..ced3b4207 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` > **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 7d7c0fa41..4c208859c 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** June 3, 2026 +> **Version:** 0.55.62 - **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 bd53587da..077a427d9 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-09-29 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** September 29, 2026 +> **Version:** 0.55.62 - **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 9933736ab..894101fc5 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** 2026-10-16 +> **Version:** 0.55.62 - **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 999616554..543037967 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-12 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** June 3, 2026 +> **Version:** 0.55.62 - **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 d7db903d0..812a47541 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.62` > **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 9bacc8a66..8cdd0a05c 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` 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/INDEX.md b/docs/INDEX.md index d797af466..be848e5c1 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-21 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** 2026-10-21 +> **Version:** 0.55.62 - **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,8 @@ current-master port from earlier isolated validation and deployed behavior. ### Start here +**Independent web Seroval/Undici correction:** [Scope, actual owners and evidence](WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md) + **Pending request-body security correction:** [Implementation and evidence](REQUEST_BODY_SECURITY_REMEDIATION.md) records proposed 0.55.61, exact API patches, CI-only locks and distinct bootstrap versus installed-graph validation. No merge or deployment is authorized. diff --git a/docs/LOGGING_AND_OBSERVABILITY.md b/docs/LOGGING_AND_OBSERVABILITY.md index ce379cb54..7ae144567 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` **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 30e39c28c..ec37b53bf 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** June 3, 2026 +> **Version:** 0.55.62 - **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 f519942c3..17ceb72fb 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` **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 d00f9d7b1..66f267360 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` > 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 6f29b1ea5..004ddd712 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Date:** May 6, 2026 +> **Version:** 0.55.62 - **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 6bc8f37d7..05cd841ea 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-23 - **Product version:** `0.55.62` 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,21 @@ notes rather than assuming that a patch label makes every rollback lossless. ## Unreleased +### 0.55.62 web Seroval and Undici security dependency patch + +This proposed patch updates exact web overrides from Seroval 1.6.3 to +1.6.11 and Undici 7.29.1 to 7.30.1. These minimal compatible patches address +the five Seroval and eight Undici advisory records listed in +[issue #256](https://github.com/pranems/SCIMServer/issues/256). +Both patches exceed the seven-day quarantine minimum. The existing API +request-body security correction remains intact; six separate API advisory +records remain open. See [ownership, scope and validation](WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md). +CI-generated locks now contain only the two intended package updates; the +restored web dependency inventory and all focused pin/owner tests pass. +Final protected validation and hosted review remain required. +No UI behavior, deployment, browser +exploitability assessment or overall release approval is included. + ### 0.55.61 request-body security dependency patch This draft patch pins body-parser to 2.4.0 and multer to 2.5.0 for the two diff --git a/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md b/docs/REMOTE_DEBUGGING_AND_DIAGNOSIS.md index 54c63dda7..f6c1a2ba5 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-09 - **Product version:** `0.55.62` > **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 4c4397769..272934823 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-09-28 - **Product version:** `0.55.62` > **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 84df300c1..1cae29283 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` > **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 160c2cdd4..6f50aa43b 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-08-12 - **Product version:** `0.55.62` -> **Version:** 0.55.61 - **Updated:** April 28, 2026 +> **Version:** 0.55.62 - **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 1829512b2..e72f77137 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.61` +> **Status:** User-facing reference - **Last verified:** 2026-10-16 - **Product version:** `0.55.62` -> **Status:** Active | **Last Updated:** 2026-10-16 | **Version:** 0.55.61 +> **Status:** Active | **Last Updated:** 2026-10-16 | **Version:** 0.55.62 > 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/WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md b/docs/WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md new file mode 100644 index 000000000..957cd35d0 --- /dev/null +++ b/docs/WEB_SEROVAL_UNDICI_SECURITY_REMEDIATION.md @@ -0,0 +1,180 @@ +# Web Seroval and Undici advisory remediation + +**Last verified:** 2026-10-29 +**Status:** 0.55.62 CI locks authenticated and restored; focused web dependency checks pass. Final protected Validate and exact-tip hosted review are pending. Not merged or deployed. + +## Executive summary + +This independent patch addresses only the web Seroval and Undici records in +[issue #256](https://github.com/pranems/SCIMServer/issues/256). +Reserve 0.55.62 from verified master +`727860866c53c7ec8d01d1ff5bae858f80a1715d` (0.55.61). +Pin Seroval 1.6.3 to 1.6.11 and Undici 7.29.1 to 7.30.1, preserving compatible +owner ranges and the existing API body-parser, multer and global iconv-lite +correction. The six remaining API advisory records are outside this outcome; +issue #256 must remain open. + +## Actual ownership and patch selection + +| Package | Actual incoming web lock edges | Minimal selected patch | Publication and quarantine eligibility (UTC) | +|---|---|---|---| +| Seroval | `@tanstack/router-core`: dependency `^1.5.4`; `seroval-plugins`: peer `^1.0` | 1.6.11 covers all five listed records, including three High; earlier listed floors 1.6.8 and 1.6.10 are insufficient collectively | Published 2026-10-18T11:47:50Z; eligible 2026-10-25T11:47:50Z | +| Undici | `jsdom`: dependency `^7.25.0` | 7.30.1 covers all eight listed records, including four High | Published 2026-10-15T14:18:31Z; eligible 2026-10-22T14:18:31Z | + +Both locks initially contain exactly one occurrence of each package. +Installed resolution from all three owner contexts confirms the old versions. +Both selected patches are compatible with the observed ranges and Node 24. +The Seroval engine minimum is Node 10; Undici requires Node >=20.18.1. +Public sha512 metadata and approved-feed availability were verified before +installation. Internal proxy tarball URLs must never enter source locks. + +Lockfile `dev` flags are not deployment or exploitability evidence. Seroval +is a web application dependency; Undici is reached through the jsdom test +stack. An image High/Critical scan cannot certify bundled browser dependency +security. This patch makes no browser exploitability or universal clearance +claim. + +## Validation and publication sequence + +1. Before changing manifests, write six exact-pin, all-occurrence and actual + installed-owner regressions. All six fail on real old versions, not a + tooling failure. Preserve the RED log and test draft in ignored + `test-results/web-advisory-remediation-20261029/`. +2. Publish the manifest/version and documentation bootstrap through the + ordinary protected hook. The test draft is not yet a source regression: + it is added after CI lock import, rather than weakening assertions or + adding a temporary skip. Bootstrap gates use the old installed tree and + do not establish patched dependency GREEN. +3. Use the unchanged `Regenerate lockfile` workflow for both workspaces. + Authenticate exact source, run and artifact digest. API changes must be + root version metadata only; web package changes must be only the two + selected patches. Verify every changed entry's age and public sha512 + provenance before import. +4. Restore through the approved registry with `npm ci --no-audit --no-fund`. + Check both lock hashes, actual installed owners and full `npm ls --all` + exit status. Add the preserved source tests and require focused GREEN. +5. Final normal Validate owns the applicable broad local suites. Require + exact-tip hosted image/smoke/Trivy, CodeQL, documentation and fresh Copilot + review. Fix relevant review comments and leave the PR in draft. + +No application behavior or UI layout is changed, so synthetic HTTP or +Playwright tests would not prove this dependency outcome. Existing tests +remain independent regression evidence; no dev/browser validation is claimed. + +## Execution issues and prevention + +**Low, evidence timestamp conversion:** continuation prose gave publication +times six hours later than its retained JSON. The confirmed mechanism was +PowerShell `ConvertFrom-Json` producing a UTC `DateTime`, followed by +`DateTimeOffset.Parse` implicitly converting it to text without its UTC kind. +Parsing that text applied the local -06:00 offset and shifted the instant +six hours forward. This was not a different registry publication time. +Fresh approved-feed metadata matches the original JSON exactly: 11:47:50Z +for Seroval and 14:18:31Z for Undici. Preserve the original UTC strings with +`JSON.parse`, or use the typed `DateTimeOffset(DateTime)` constructor rather +than reparsing a date through text. A boundary negative control reproduces +the old method rejecting a package one second beyond seven days; the typed +conversion accepts it, and two UTC boundary assertions pass. Corrected +calculations leave all five assessed package fixes eligible, so no adoption +decision changed. Metadata preflight first detected the discrepancy; the +follow-up isolated its conversion mechanism. Prevent recurrence by preserving +UTC identity and checking both sides of the actual seven-day threshold. + +**Confirmed and fixed failure (High, dependency pin drift):** exact overrides +retained vulnerable releases after their original fixes aged. The focused +regressions fail 6/6: expected Seroval 1.6.11, received 1.6.3; expected Undici +7.30.1, received 7.29.1. Detection is the initial focused unit lane, the earliest +applicable stage. Authenticated lock import and clean restore resolve all six +assertions; the combined existing/new dependency specs pass 35/35. +The exact pins remove the vulnerable versions from every actual web lock +occurrence and all installed owner contexts, not merely manifest metadata. +Those initial 35 tests were run in API locally; the six new checks now live +in the web workspace for the CI-order reason below. + +**Confirmed and fixed test-lane failure (Medium, CI prerequisite ordering):** +source run 40899500545 and PR run 40899546495 fail four installed-web checks +with `ENOENT`, because the unchanged CI workflow runs API units before its +web install step. Both installed trees existed locally, hiding that invalid +prerequisite assumption. The fix moves all six new regressions to +`web/src/test/web-advisory-pins.test.ts` in the existing web Vitest lane, +which runs after web installation. Native `createRequire` resolves the same +actual owner contexts there. Focused web GREEN passes all six assertions +without changing a workflow, skipping a test or relaxing an assertion. +Detection: hosted API unit stage; earliest feasible detection: source review +of each test's installation prerequisites before first publication. +Prevention is structural: installed-package checks belong to their owning +workspace's existing post-install test lane. The original source/PR failures +and local success remain retained, not relabeled as intentional RED. + +**Pre-existing API inventory finding (Medium, separate follow-up):** full `npm ls --all` +reports the two intentionally stale web packages after the manifest edit. +It also reports API `debug`, `get-intrinsic` and `call-bind-apply-helpers` +invalid in the existing installed tree, although no API override or locked +package was changed. A clean API restore of the authenticated unchanged +package graph retains the same three invalidity classes. Top-level API +`npm ls` exits 0 but is not full-tree clearance. This patch does not change +API overrides or label the errors harmless. Web `npm ls --all` exits 0 with +no problems after its clean restore. The initial and restored failure logs +are retained separately from intentional pin RED. Detection was full +inventory preflight, earlier than broad tests; a top-level-only inventory +would miss these owner-context errors. Track diagnosis with the independent +API work in issue #256 (owner: repository maintainer; target: 2026-11-05). + +## Authenticated CI locks and measured results + +| Evidence | Result | +|---|---| +| Regeneration | [Run 40895972413](https://github.com/pranems/SCIMServer/actions/runs/40895972413), SUCCESS; unchanged workflow, both workspaces; exact source `419596869f3f2de0aa605290d958dcda7ad57e80` | +| Artifact authentication | `regenerated-lockfiles`, ID 12906712562; downloaded ZIP SHA-256 `a84a0114a0cd91bcb2596815fa7ce72f0711b65f3c8778f802f4ad759980e928`, equal to GitHub's authenticated artifact digest | +| Entire graph delta | API has zero non-root package changes; web has only Seroval 1.6.3 -> 1.6.11 and Undici 7.29.1 -> 7.30.1. Both root versions become 0.55.62 | +| CI publication ages | Checked 2026-10-29T14:53:28.243Z: Seroval 11.1289 days, Undici 14.0243 days. CI's original registry timestamps also retain milliseconds; approved-feed timestamps agree to seconds | +| API artifact/after-restore SHA-256 | `7e0a61ca78c652ceac6239a4db5ac92ca597cab7a5a9ad8041ecdeb08277a087` | +| Web artifact/after-restore SHA-256 | `312b4337d1571c0f962178272d5b7330b453cd6994686a57b90dabd962ce1ecb` | +| Provenance and metadata | Every non-root entry uses public registry URLs and sha512; both changed integrity values equal the approved exact-version metadata | +| Clean restores | `npm --prefix api ci --no-audit --no-fund` and `npm --prefix web ci --no-audit --no-fund`: PASS; both lockfiles byte-identical to their CI artifacts afterward | +| Initial focused GREEN, before CI-order correction | API `npm test -- --runInBand --runTestsByPath src/scripts/web-advisory-pins.spec.ts src/scripts/check-dependency-pins.spec.ts`: 35/35 in two suites on a machine with both installations; not proof of CI prerequisite ordering | +| Correct workspace focused GREEN | Web `npm test -- src/test/web-advisory-pins.test.ts`: all six preserved regressions PASS after relocation | +| Actual web inventory | `npm --prefix web ls --all --json`: exit 0, no problems. Both actual Seroval owners and jsdom's Undici resolve the intended patches | +| Advisory scan | `npm --prefix api run check:pins`: web eight pins, zero findings; overall exit 1 correctly retains six separate API advisory records. No scanner suppression or blanket clearance | +| Bootstrap hook only | Normal Fast push at bootstrap source: 25/25 gates, 618.2 seconds; units 6,220, HTTP 2,514 plus 42 skips. The existing HTTP worker warning remains distinct from unit fixture correctness | + +Baseline locks in this Windows checkout use CRLF, whereas CI artifacts and +Git source use LF. Normalizing the baseline's line endings matches the +parent-authenticated source hashes exactly: API +`b8b3b3e0395879222e19e7a95c2dc1eb8ee1be8b9f6514eb3f06f32c9f67be1e`, +web `b0ae8b65b876e51f7c915df2f7f6586f05192cade4094ba0109430acca57252a`. +No baseline package content changed before CI import. + +Source `52200e33` passed the normal local Validate hook (28/28, 783.1 +seconds; units 6,226, HTTP 2,514 plus 42 skips, web 1,655), then the +independent hosted lane correctly caught the installation-order error. +Those are historical results, not final-tip proof. The correcting source +must pass a fresh normal Validate and exact-tip hosted checks. + +**Self-improvement: applied.** The regression checks every actual lock +occurrence and resolves from every installed owner, so a valid-looking root +pin cannot conceal a stale nested package or incompatible override context. +The existing full npm inventory remains required separately. + +**Design disposition: accepted.** Two exact overrides use existing tooling. +No new strategy, runtime abstraction, policy exception or gate redesign is +justified. The broader browser-bundle scan coverage question is a bounded +follow-up, not part of this patch. +Owner: repository maintainer; target: next dependency-gate review by +2026-11-05. Evaluate bundled browser dependency scanning separately from +runtime-image scanning without introducing an exception here. + +## Limitations and handoff + +This is reconstructed continuation, not a restored deleted transcript. +Source evidence is the focused logs and this patch's command receipts; no +claim is made that an unavailable earlier transcript was reconciled. +Final receipt path: +`test-results/web-advisory-remediation-20261029/HANDOFF.md`. + +No merge, deployment, release admission or overall security clearance is +authorized. Preserve performance ratio 1.23928982 >1.10, customer data/restore, +cleanup 403, measured browser/human visual verification, native/OS Windows +delay and competing Mermaid renderer holds. PR #251 remains untouched. +Repository CodeQL's 107 pre-existing alerts are distinct from no-new-alert +checks on this patch. diff --git a/web/package-lock.json b/web/package-lock.json index a5ca35c05..fbd7c4b32 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "scimserver-web", - "version": "0.55.61", + "version": "0.55.62", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scimserver-web", - "version": "0.55.61", + "version": "0.55.62", "dependencies": { "@fluentui/react-components": "^9.74.2", "@fluentui/react-icons": "^2.0.331", @@ -5349,9 +5349,9 @@ } }, "node_modules/seroval": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.3.tgz", - "integrity": "sha512-5PwLbVPpT4DEki1hYVSSsoxWY6xCNMVucAVLK/CH0mXSDfp/I4KaAex72q80GNt5hpn7CCMGdSpIg4YGqH/wXQ==", + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.11.tgz", + "integrity": "sha512-hpb6XnMCGN/A+79Uf8V7/iTGkyyyazNn5X0CoCAp7Uy9vT3RT8jZ9GLcnxGoVjg3P8AArbe4ZVHq6C8oT2a0kA==", "license": "MIT", "engines": { "node": ">=10" @@ -5688,9 +5688,9 @@ } }, "node_modules/undici": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz", - "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==", + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", "dev": true, "license": "MIT", "engines": { diff --git a/web/package.json b/web/package.json index 9ca6d786a..4147f5218 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,7 @@ { "name": "scimserver-web", "private": true, - "version": "0.55.61", + "version": "0.55.62", "type": "module", "scripts": { "dev": "vite", @@ -53,9 +53,9 @@ "nanoid@^3.0.0": "3.3.18", "nanoid@^5.0.0": "5.1.16", "postcss": "8.5.23", - "seroval": "1.6.3", + "seroval": "1.6.11", "source-map-js": "1.2.2", - "undici": "7.29.1", + "undici": "7.30.1", "ws": "8.21.0" }, "size-limit": [ diff --git a/web/src/test/web-advisory-pins.test.ts b/web/src/test/web-advisory-pins.test.ts new file mode 100644 index 000000000..513bf09e9 --- /dev/null +++ b/web/src/test/web-advisory-pins.test.ts @@ -0,0 +1,76 @@ +import fs from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; + +type PackageEntry = { + version?: string; + resolved?: string; + integrity?: string; + dependencies?: Record; + optionalDependencies?: Record; + peerDependencies?: Record; +}; + +// CI installs web dependencies after its API unit-test lane. +describe('web Seroval and Undici remediation (#256)', () => { + const web = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); + const require = createRequire(import.meta.url); + const patches = [ + { name: 'seroval', version: '1.6.11', owners: [ + ['node_modules/@tanstack/router-core', 'dependencies', '^1.5.4'], + ['node_modules/seroval-plugins', 'peerDependencies', '^1.0'], + ] }, + { name: 'undici', version: '7.30.1', owners: [ + ['node_modules/jsdom', 'dependencies', '^7.25.0'], + ] }, + ]; + + it.each(patches)('pins $name to its minimal exact patch', async ({ name, version }) => { + const manifest = JSON.parse(await fs.readFile(resolve(web, 'package.json'), 'utf8')) as { + overrides: Record; + }; + expect(manifest.overrides[name]).toBe(version); + }); + + it.each(patches)('checks every locked and installed $name occurrence', async ({ name, version }) => { + const lock = JSON.parse(await fs.readFile(resolve(web, 'package-lock.json'), 'utf8')) as { + packages: Record; + }; + const entries = Object.entries(lock.packages).filter(([path]) => + path === `node_modules/${name}` || path.endsWith(`/node_modules/${name}`), + ); + expect(entries.length).toBeGreaterThan(0); + for (const [path, entry] of entries) { + expect(entry.version).toBe(version); + expect(entry.resolved).toBe(`https://registry.npmjs.org/${name}/-/${name}-${version}.tgz`); + expect(entry.integrity).toMatch(/^sha512-[A-Za-z0-9+/]+=*$/); + const installed = JSON.parse( + await fs.readFile(resolve(web, path, 'package.json'), 'utf8'), + ) as { version: string }; + expect(installed.version).toBe(version); + } + }); + + it.each(patches)('resolves $name from every actual owner context', async ({ name, version, owners }) => { + const lock = JSON.parse(await fs.readFile(resolve(web, 'package-lock.json'), 'utf8')) as { + packages: Record; + }; + const actualOwners = Object.entries(lock.packages).flatMap(([path, entry]) => + (['dependencies', 'optionalDependencies', 'peerDependencies'] as const).flatMap(kind => { + const range = entry[kind]?.[name]; + return range ? [[path, kind, range]] : []; + }), + ); + expect(actualOwners).toEqual(owners); + for (const [owner, kind, range] of actualOwners) { + const ownerPath = resolve(web, owner, 'package.json'); + const installedOwner = JSON.parse(await fs.readFile(ownerPath, 'utf8')) as PackageEntry; + expect(installedOwner[kind as 'dependencies' | 'optionalDependencies' | 'peerDependencies']?.[name]).toBe(range); + const installedPath = require.resolve(`${name}/package.json`, { paths: [dirname(ownerPath)] }); + const installed = JSON.parse(await fs.readFile(installedPath, 'utf8')) as { version: string }; + expect(installed.version).toBe(version); + } + }); +});