toga-ai 1.0.821 → 1.0.822

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,12 +64,21 @@ lands in `api()`.
64
64
 
65
65
  `api()` is the protocol router and the **transaction/logging boundary**:
66
66
  - **CORS (2026-09-16):** **reflects the caller's `Origin` header** back in
67
- `Access-Control-Allow-Origin` (per-request echo) instead of a wide-open `*`, and sends
68
- `Access-Control-Allow-Credentials: true`; still returns 200 for the `OPTIONS` preflight. A
69
- wildcard `*` cannot be combined with credentialed requests, so echoing the Origin is what lets
70
- a future cookie/session flow work without reopening the hole but it is **not** an allowlist:
71
- every Origin is still reflected. Tighten to a real allowlist before adding any cookie/session
72
- auth here.
67
+ `Access-Control-Allow-Origin` when `$_SERVER['HTTP_ORIGIN']` is present and matches a strict
68
+ shape regex `^https?://[A-Za-z0-9.\-]+(:\d+)?$` (with the `D` end-anchor flag); otherwise it
69
+ falls back to `*`. It also emits `Vary: Origin`. This block sits **before** the `OPTIONS`
70
+ preflight short-circuit (still 200 for `OPTIONS`). The reflected value must be the **whole
71
+ origin `scheme://host[:port]` (e.g. `http://compass.togacommerce`)** because the browser
72
+ compares it byte-for-byte; do **not** strip the scheme or "clean" `HTTP_ORIGIN` (it is an
73
+ origin, not a URL with a path). Why: some higher-security client networks strip or reject a
74
+ wildcard `*`, so a client's app fails with a CORS "no Access-Control-Allow-Origin" error
75
+ **inside** their network while working fine outside it. **Security:** reflecting any
76
+ well-formed origin is effectively equivalent to `*` here because auth is **Bearer-token, never
77
+ cookies** — no new exposure; the regex is a strict allowlist that blocks header injection, and
78
+ PHP `header()` also rejects CR/LF. **`Access-Control-Allow-Credentials` is deliberately NOT set
79
+ — do NOT add it on top of this reflection without a cookie-auth security review; that
80
+ combination would be a cross-site data-theft hole.** `Vary: Origin` stops a shared cache
81
+ serving one origin's header to another.
73
82
  - **`/health`:** 200 + empty object (EB/LB probe — keep it working).
74
83
  - **Sentry:** initialized per request; EC2 instance metadata attached.
75
84
  - **Core DB bootstrap (now guarded, 2026-07-23):** registers `Core` (`DB_CORE`), preferring a
@@ -396,13 +405,15 @@ they are the known sharp edges. Do not re-discover these from scratch.
396
405
  `Core.Database` row (`id = CORE_LOGS_DATABASE_ID`); a local Logs DB whose actual schema name
397
406
  differs throws `Unknown database`. This is environment config, not a code bug — fix the row or
398
407
  the local schema name, don't patch the bootstrap.
399
- 5. **CORS reflects the caller's `Origin` (not an allowlist) (updated 2026-09-16).** The front
400
- controller now echoes the request `Origin` into `Access-Control-Allow-Origin` and sends
401
- `Access-Control-Allow-Credentials: true` (was a wide-open `Access-Control-Allow-*`). This
402
- retires the old "wide-open CORS" risk for the current bearer-token auth, but it is **still not
403
- a restriction** every Origin is reflected. If any cookie/session-backed auth is ever added
404
- here, replace the Origin echo with a real hardcoded allowlist first; reflecting the Origin
405
- while sending credentials would otherwise be an exploitable hole.
408
+ 5. **CORS reflects the caller's `Origin`, falling back to `*` (updated 2026-09-16).** The front
409
+ controller now echoes the request `Origin` into `Access-Control-Allow-Origin` when it matches a
410
+ strict shape regex, else falls back to `*`, and adds `Vary: Origin` (was a wide-open
411
+ `Access-Control-Allow-*`). Safe **only** because auth is Bearer-token, never cookie/session
412
+ `Access-Control-Allow-Credentials` is intentionally **unset**, and reflecting the origin is
413
+ effectively equivalent to `*` here (no new exposure). It is a shape check, **not** a trusted-
414
+ origin allowlist every well-formed Origin is reflected. If any cookie/session-backed auth is
415
+ ever added here, do not set credentials and re-tighten CORS to a real hardcoded allowlist first;
416
+ origin-reflection + credentials would be a cross-site data-theft hole.
406
417
  6. **`_underscore` is cloned at build from a moving branch** (`_<ENVIRONMENT>`), not pinned to a
407
418
  commit. Two deploys of the same api2 commit can produce different runtime behavior. Check the
408
419
  framework branch state when triaging an "it worked yesterday" regression.
@@ -457,13 +468,17 @@ they are the known sharp edges. Do not re-discover these from scratch.
457
468
 
458
469
  ## Change history
459
470
  - 2026-09-16 — **CORS now reflects the caller's `Origin`** instead of a wide-open
460
- `Access-Control-Allow-*`: the front controller (`Controller/Index.php`) echoes the request
461
- `Origin` into `Access-Control-Allow-Origin` and sends `Access-Control-Allow-Credentials: true`,
462
- still returning 200 for the `OPTIONS` preflight. Updated the front-controller CORS bullet and
463
- **retired the old "CORS is fully permissive" wide-open risk** (Known issue #5) — reframed to
464
- note the header is now a per-request Origin echo, **not** an allowlist, so it must still be
465
- replaced with a hardcoded allowlist before any cookie/session-backed auth is added here.
466
- (jcardinal)
471
+ `Access-Control-Allow-*`: the front controller (`Controller/Index.php`, `_Controller_Index::api()`,
472
+ before the `OPTIONS` short-circuit) echoes the request `Origin` into `Access-Control-Allow-Origin`
473
+ when `HTTP_ORIGIN` matches `^https?://[A-Za-z0-9.\-]+(:\d+)?$` (`D` flag), else falls back to `*`,
474
+ and adds `Vary: Origin` (whole origin incl. scheme browser compares byte-for-byte). Motivation:
475
+ some higher-security client networks strip/reject a wildcard `*`, so a client failed with a CORS
476
+ "no Access-Control-Allow-Origin" error inside their network while it worked outside. Retires Known
477
+ issue #5. `Access-Control-Allow-Credentials` deliberately **NOT** set (auth is Bearer-token, never
478
+ cookies — reflection is equivalent to `*` here with no new exposure; adding credentials on top
479
+ would be a cross-site data-theft hole and needs a cookie-auth review). Reviewed clean by
480
+ php-reviewer (valid PHP 8.2) and cso (SAFE TO SHIP). Note: the code change is a working-tree edit,
481
+ not yet committed. (jcardinal)
467
482
  - 2026-09-01 — Recorded a **sixth EB-`Degraded` cause — a client 4xx BURST**: EB Enhanced Health
468
483
  counts application/ELB 4xx toward env health via two default-Enabled `ConfigDocument` rules
469
484
  (`ApplicationRequests4xx`, `ELBRequests4xx`, namespace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.821",
3
+ "version": "1.0.822",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",