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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### 0.55.66 reviewed-master integration (2026-10-30)

- Normally integrate reviewed master `cffc64c8` after #259, #260 and #261
merged. Preserve original Hono head `7379a97e` and both documentation
histories. No further dependency version or runtime behavior is added.
- Keep the actual-parent-qualified Hono 2.1.3 override and all incoming
inventory, qs, fast-uri and web dependency corrections. Both roots retain
the reserved 0.55.66 version. New-head graph, restore, protected gates,
clean-CI bytes, independent review and hosted results remain required.
- Parent #261 passed 28/28 gates with API 6,248/215 suites, HTTP 2,514/118
plus 42 skips and web 1,661/125 files; these are parent provenance, not
assumed new-head results. Performance #251 and every final-artifact hold
remain separate. No benchmark or deployment was started.
- Self-improvement applied: explicit parent/source/complete-graph preservation.
Design accepted: ordinary integration, no new responsibility or abstraction.

### 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.
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.
- [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 reviewed-master integration (2026-10-29)

- Normally integrate reviewed master `287ea671` after #259 and #260 merged.
Expand Down
2 changes: 1 addition & 1 deletion DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -9,10 +9,14 @@ cover 0.55.58 correction preparation, merged 0.55.57 source, the deployed 0.55.5
cumulative changes since 0.55.20, migration/rollback steps and known limits.
See [CHANGELOG.md](CHANGELOG.md) for detailed engineering history.

**Pending API advisory integration:** [Consumer compatibility and remaining Hono blocker](docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md)
describes proposed 0.55.64 integrating reviewed master after #259 and #260
merged. Five assessed advisory records are targeted; this proposal is not
a deployed release or full advisory-clearance claim.
**Current Hono source integration:** [Actual Prisma parent qualification](docs/HONO_PRISMA_PARENT_QUALIFICATION.md)
records proposed 0.55.66 integrating reviewed master after #259, #260 and
#261 merged. It preserves the bounded adapter compatibility proof and all
dependency fixes. New-head validation and review remain required; no deployed
release or full advisory/performance admission is claimed.

**Merged API advisory source:** [Consumer compatibility and historical Hono boundary](docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md)
records the 0.55.64 qs and fast-uri correction merged at `cffc64c8`.

**Merged source-only inventory correction:** [API override-context validity](docs/API_OVERRIDE_CONTEXT_VALIDITY.md)
describes merged 0.55.63 source and the complete-tree npm regression. It
Expand All @@ -25,7 +29,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)]()
Expand Down
48 changes: 46 additions & 2 deletions Session_starter.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,52 @@
## SCIMServer - Condensed Session Memory

**Last verified:** 2026-10-29
**Last verified:** 2026-10-30

### Current Hono reviewed-master integration (2026-10-30)

PR #261 merged normally at `cffc64c8a188a721fabb85c37a80243d5e65a74f`,
after 28/28 protected Validate gates, seven exact-head checks and separate
technical review. Its counts were API 6,248/215 suites, HTTP 2,514/118 suites
plus 42 existing skips, and web 1,661/125 files. #259 and #260 are merged.

The preserved 0.55.66 Hono candidate now normally integrates that master.
Five documentation conflicts retain both preparation histories below.
Original Hono head `7379a97e` and its actual-parent qualification remain
preserved. Source/graph preservation and the owned API-only clean restore
pass with both lock byte hashes unchanged; both complete installed inventories
exit 0 and isolated Prisma generation passes. Protected Validate, clean-CI
lock confirmation, separate review and hosted checks remain required.
Performance #251 remains unmerged with a verified same-image control failure;
no repeated campaign, threshold waiver or deployment follows from this merge.
If 0.55.66 merges first, the held 0.55.65 candidate must not downgrade it.

### Current advisory integration (2026-10-29)
| Date | Summary |
|---|---|
| 2026-10-30 | Normally integrate reviewed advisory master into independent Hono #262 at its reserved 0.55.66 boundary; retain both histories and all release holds. |

### Historical 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).
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 |
|---|---|
| 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. |

### Historical advisory integration (2026-10-29)

PR #260 merged normally at `287ea671abf1619d7b172c495310769078bd685b`
after 28/28 protected Validate gates, seven successful exact-head checks and
Expand Down
12 changes: 6 additions & 6 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down
121 changes: 121 additions & 0 deletions api/src/scripts/check-hono-parent-qualification.spec.ts
Original file line number Diff line number Diff line change
@@ -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<string, string>;
};
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<string, Package>;
};
const manifest = JSON.parse(readFileSync(resolve(apiRoot, 'package.json'), 'utf8')) as {
overrides: Record<string, string>;
};
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([]);
});
});
22 changes: 22 additions & 0 deletions docs/API_ADVISORY_CONSUMER_COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@

## Executive summary

**Current source boundary (2026-10-30):** the 0.55.64 advisory correction
merged normally as `cffc64c8`. Separate Hono #262 now integrates that reviewed
master at proposed 0.55.66, preserving its actual-parent qualification and
both historical accounts below. New-head verification, review and CI remain
required. No deployment, full advisory clearance or performance admission.
The earlier Hono blocker below describes the 0.55.64 rollback unit, not an
assertion that this separate Hono proposal lacks actual-parent evidence.

### Historical independent advisory preparation

**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
merged. PR #259 and #251 remain separate. No deployed version changes.

### Historical reviewed-master advisory integration

Proposed product 0.55.64 now normally integrates reviewed master
`287ea671abf1619d7b172c495310769078bd685b`, after #259 and #260 merged.
It preserves original advisory head `f4fcfe3c` and every constituent commit.
Expand Down
2 changes: 1 addition & 1 deletion docs/AUTHENTICATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -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.
>
Expand Down
4 changes: 2 additions & 2 deletions docs/AZURE_DEPLOYMENT_AND_USAGE_GUIDE.md
Original file line number Diff line number Diff line change
@@ -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/)

---
Expand Down
4 changes: 2 additions & 2 deletions docs/COMPLETE_API_REFERENCE.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
Loading
Loading