@ai-ecoverse/slicc 1.39.1 → 1.39.3

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.
Files changed (2) hide show
  1. package/README.md +21 -5
  2. package/package.json +8 -8
package/README.md CHANGED
@@ -192,7 +192,7 @@ A profile that doesn't exist is an error (`SLICC six's Chrome profile doesn't ex
192
192
  - **Live and not debuggable:** slicc stops with exit 1: `six's Chrome is using this profile; close it, or use --browser/--profile to pick another`.
193
193
  - **Free** (no lock, or a stale one): slicc launches the browser six used. That is the installed Chromium browser whose version is the profile's `Last Version` (or, without one, the version in `Local State`'s `user_experience_metrics.stability.stats_version`), the first in `slicc browsers`' id order when several match; otherwise Chrome stable. `--browser <id>` overrides it (for `slicc`, the first `--browser`; the others are extras as usual).
194
194
 
195
- **Nothing is written to six's profile.** No seven prefs, no clean-exit marking, no removal of `DevToolsActivePort`, `Singleton*`, the session snapshot or any other file, no extensions added or removed, and no migration. The launch passes only runtime flags, which don't persist: `--remote-debugging-port=0 --user-data-dir=<six's profile> --no-first-run --no-default-browser-check`, the seven flags (left out with `slicc launch --no-seven-flags`), `--headless=new` with `--headless`, and the start page. A `DevToolsActivePort` that was there before the launch is ignored until Chrome writes a new one.
195
+ **Nothing is written to six's profile.** No seven prefs, no clean-exit marking, no removal of `DevToolsActivePort`, `Singleton*`, the session snapshot or any other file, no extensions added or removed, and no migration. The launch passes only runtime flags, which don't persist: `--remote-debugging-port=0 --user-data-dir=<six's profile> --no-first-run --no-default-browser-check`, the throwaway flags only when six's profile is in the temp dir (see [`slicc launch`](#slicc-launch---browser-id---profile-name---url-url---headless---no-seven-flags---json)), the seven flags (left out with `slicc launch --no-seven-flags`), `--headless=new` with `--headless`, and the start page. A `DevToolsActivePort` that was there before the launch is ignored until Chrome writes a new one.
196
196
 
197
197
  **What does persist** is only what Chrome itself keeps: browsing data from the session, and the seven tab in session restore, so six may reopen that tab the next time it starts Chrome.
198
198
 
@@ -480,7 +480,8 @@ Launches a browser with CDP and keeps running until interrupted (Ctrl-C, `SIGINT
480
480
  - `slicc/profiles` is never scanned or launched into afterwards.
481
481
  - Before the launch, a stale `DevToolsActivePort`, Chrome's `Singleton*` files and the session-restore snapshot are removed, and the last exit is marked clean, so only the start page opens.
482
482
  - Before each launch, the seven prefs are merged into the profile's `Default/Preferences`, which is created if missing: `tab_freezing_enabled: false`, `performance_tuning.tab_freezing.enabled: false`, Memory Saver off (`performance_tuning.high_efficiency_mode.state: 0`), and `[*.]sliccy.ai` plus each extra allowed origin's host as tab-discard exceptions. The exceptions go into the list `performance_tuning.tab_discarding.exceptions` (six's form) and into the map `performance_tuning.tab_discarding.exceptions_with_time`, which Chrome 154 reads and shows under "Always keep these sites active". Existing keys, exceptions and timestamps are kept, and a file that already has everything isn't rewritten. A Preferences file that isn't a JSON object is left alone. `--no-seven-flags` skips these prefs too.
483
- - Chromium flags: `--remote-debugging-port=0 --user-data-dir=<profile> --no-first-run --no-default-browser-check`, the seven flags below, `--headless=new` with `--headless`, then `--url`.
483
+ - Chromium flags: `--remote-debugging-port=0 --user-data-dir=<profile> --no-first-run --no-default-browser-check`, the throwaway flags for a throwaway profile, the seven flags below, `--headless=new` with `--headless`, then `--url`.
484
+ - A throwaway profile is one inside the system temp dir (Go's `os.TempDir()`: `TMPDIR` on macOS and Linux, `TMP` or `TEMP` on Windows; symlinks resolved, case ignored on macOS and Windows). It gets `--use-mock-keychain --password-store=basic` (`browser.ThrowawaySwitches`), so Chrome never asks for the macOS keychain or a Linux password store; without a default keychain, as under a temporary `HOME`, macOS would show "Keychain Not Found". Every other profile, SLICC's and six's included, headless or not, keeps the real keychain, so its saved cookies and passwords stay readable. `browser.Throwaway(dir)` tells which is which.
484
485
  - The seven flags keep a seven tab's kernel running while the tab is in the background: `--disable-background-timer-throttling`, `--disable-backgrounding-occluded-windows`, `--disable-renderer-backgrounding`, `--disable-background-tracing`, and `--disable-features=LocalNetworkAccessChecks,LocalNetworkAccessChecksWebSockets,IntensiveWakeUpThrottling,HighEfficiencyModeAvailable,InfiniteTabsFreezing,InfiniteTabsFreezingOnMemoryPressure,CPUMeasurementInFreezingPolicy,MemoryMeasurementInFreezingPolicy,AllowDevtoolsConnectedDiscard`. Chromium ignores feature names it doesn't know. `--no-seven-flags` (`LaunchOptions.NoSevenFlags`) leaves them all out, along with the seven prefs above. Chromium keeps only one `--disable-features` value, so every `--disable-features` from the seven flags and from `LaunchOptions.ExtraArgs` is merged into one switch, in order and without duplicates. The CDP port is read from the profile's `DevToolsActivePort` once `/json/version` answers with the same websocket path (30 s limit). On macOS a browser with a window starts through `open -n -W -a <app> --args …`, so camera and microphone permissions belong to the browser, not the terminal; `--headless` runs the executable directly. A flatpak starts with `flatpak run --filesystem=<profile> <id>`.
485
486
  - Electron apps keep their own data and get `--remote-debugging-port=<port>`, the first free port from 9333 up, so they never take 9222–9229. An app that is already running is refused, since a second instance would hand off to it; quit it first. Apps with `debugSupport: "disabled"` are refused.
486
487
  - Shutdown: `Browser.close` over CDP; if the browser is still running 5 s later, a signal (`SIGTERM` to the browser's process group and main process; `taskkill /T` on Windows), then after 3 s a kill (`SIGKILL`; `taskkill /F /T`). The browser runs in its own process group (`CREATE_NEW_PROCESS_GROUP` on Windows), so a terminal Ctrl-C reaches only `slicc`.
@@ -759,7 +760,12 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
759
760
  - **Refused**, with `{"id":…,"sessionId"?:…,"error":{"code":-32000,"message":"slicc: refused: target is on seven's origin"}}`, never forwarded: `Target.createTarget`, `Page.navigate` and anything else whose `url` is protected; `Page.navigateToHistoryEntry` to an entry whose URL is protected (the node looks it up with `Page.getNavigationHistory`); `Target.attachToTarget`, `activateTarget`, `closeTarget`, `autoAttachRelated`, `Browser.getWindowForTarget` and any other command whose `targetId` or `frameId` is a protected target; and every command on a session whose target is protected, or became so. `Target.getTargetInfo` and `Target.detachFromTarget` stay allowed.
760
761
  - **Origins and storage.** A command whose `origin`, `securityOrigin`, `ownerOrigin`, `issuerOrigin`, `topLevelOrigin`, `topLevelSite`, `embeddedOrigin`, `requestingOrigin`, `scopeURL` or `manifestId` is protected, or whose `storageKey` or `cacheId` names seven's storage, is refused the same way. This covers `Storage.*`, `IndexedDB.*`, `CacheStorage.*`, `DOMStorage.*`, `FileSystem.*`, `ServiceWorker.*`, `PWA.*`, `Browser.setPermission` and `Browser.grantPermissions`. Free-form values (`expression`, `arguments`, `value`, `headers`, `postData` and the like) aren't searched.
761
762
  - **Cookies.** `Network.getCookies`, `Network.getAllCookies`, `Storage.getCookies` and `Page.getCookies` answer without the cookies whose domain is protected (`sliccy.ai` and every subdomain, and each extra origin's host and its parents: cookies ignore ports), and without partitioned cookies whose `partitionKey.topLevelSite` is such a site. `Network.setCookie`, `setCookies`, `deleteCookies`, `Storage.setCookies` and `Page.deleteCookie` for such a domain, a URL on such a host, or a partition key under such a site, are refused with `slicc: refused: cookies on seven's origin`. So are `Network.clearBrowserCookies` and `Storage.clearCookies`, except in a browser context this stream created with `Target.createBrowserContext`.
762
- - **Requests a page makes to seven.** For a request whose URL is protected, `Network.*` and `Fetch.*` events lose their `Cookie` and `Set-Cookie` headers, their raw header texts and their cookie lists, and `requestWillBeSentExtraInfo` drops seven's cookies from `associatedCookies` for any request. Chrome can send `requestWillBeSentExtraInfo` and `responseReceivedExtraInfo` before the event that names the request's URL, so the node holds them until the request is found to be protected, gets its response, finishes or fails (a WebSocket or WebTransport once it is created); one still held after 2 seconds is passed on stripped, as for a protected request. For a WebSocket, WebTransport or EventSource on a protected origin (`ws:` and `wss:` count as `http:` and `https:`), `webSocketWillSendHandshakeRequest`, `webSocketHandshakeResponseReceived`, `webSocketFrameError`, `eventSourceMessageReceived` and `dataReceived` are dropped. Reading such a request's body (`Network.getResponseBody`, `Fetch.getResponseBody`, `takeResponseBody…AsStream`, `Audits.getEncodedResponse` and the like) is refused. The node also checks the answer, because the page may ask before the node has seen the request. When `Network.getResponseBody`, `searchInResponseBody`, `streamResourceContent`, `getResponseBodyForInterception`, `takeResponseBodyForInterceptionAsStream`, `getRequestPostData`, `Fetch.getResponseBody`, `Fetch.takeResponseBodyAsStream` or `Audits.getEncodedResponse` answers, the node holds the answer until the request has a response (for `getRequestPostData`, until it is sent; for an intercepted request, until it is paused). If the request is protected by then, the answer is refused, and a stream it handed out is closed. If the node still hasn't seen the request after 2 seconds, the answer is refused with `slicc: refused: slicc hasn't seen this request, so it could be seven's`. `Network.replayXHR` is refused with that message too, unless the node has already seen the request; for a protected request it is refused like the body getters. `IO.read` is refused, with `slicc: refused: slicc didn't hand out this stream, so it could be seven's`, unless the node passed that handle to the page in an allowed answer (`take…AsStream`, `Page.printToPDF`, `Network.loadNetworkResource`, or `IO.resolveBlob` as `blob:<uuid>`). Continuing, failing or fulfilling it is allowed, except for a document request, which the node fails itself (see the next items).
763
+ - **Requests a page makes to seven.** For a request whose URL is protected, `Network.*` and `Fetch.*` events lose their `Cookie` and `Set-Cookie` headers, their raw header texts and their cookie lists, and `requestWillBeSentExtraInfo` drops seven's cookies from `associatedCookies` for any request. Chrome can send `requestWillBeSentExtraInfo` and `responseReceivedExtraInfo` before the event that names the request's URL, so the node holds them until the request is found to be protected, gets its response, finishes or fails (a WebSocket or WebTransport once it is created); one still held after 2 seconds is passed on stripped, as for a protected request. For a WebSocket, WebTransport or EventSource on a protected origin (`ws:` and `wss:` count as `http:` and `https:`), `webSocketWillSendHandshakeRequest`, `webSocketHandshakeResponseReceived`, `webSocketFrameError`, `eventSourceMessageReceived` and `dataReceived` are dropped. Reading such a request's body (`Network.getResponseBody`, `Fetch.getResponseBody`, `takeResponseBody…AsStream`, `Audits.getEncodedResponse` and the like) is refused. The node also checks the answer, because the page may ask before the node has seen the request. When `Network.getResponseBody`, `searchInResponseBody`, `streamResourceContent`, `getResponseBodyForInterception`, `takeResponseBodyForInterceptionAsStream`, `getRequestPostData`, `Fetch.getResponseBody`, `Fetch.takeResponseBodyAsStream` or `Audits.getEncodedResponse` answers, the node holds the answer until the request has a response (for `getRequestPostData`, until it is sent; for an intercepted request, until it is paused). If the request is protected by then, the answer is refused, and a stream it handed out is closed. If the node still hasn't seen the request after 2 seconds, the answer is refused with `slicc: refused: slicc hasn't seen this request, so it could be seven's`. `Network.replayXHR` is refused with that message too, unless the node has already seen the request; for a protected request it is refused like the body getters. `IO.read` is refused, with `slicc: refused: slicc didn't hand out this stream, so it could be seven's`, unless the node passed that handle to the page in an allowed answer (`take…AsStream`, `Page.printToPDF`, `Network.loadNetworkResource`, or `IO.resolveBlob` as `blob:<uuid>`). - **Paused requests to seven.** A page session's own interception never gets to touch a request whose URL is protected, whatever its kind (a document, `fetch()`, XHR, an image or any other subresource), at either stage. The node keeps `Fetch.requestPaused`, `Fetch.authRequired` and the deprecated `Network.requestIntercepted` for such a request from the page and settles it itself:
764
+ - a document request is failed (`Fetch.failRequest` with `BlockedByClient`, or `Network.continueInterceptedRequest` with `errorReason: BlockedByClient`);
765
+ - any other request continues untouched (`Fetch.continueRequest`, or `Network.continueInterceptedRequest` with only its `interceptionId`);
766
+ - an auth challenge gets the browser's own handling (`Fetch.continueWithAuth`, or `Network.continueInterceptedRequest`, with `authChallengeResponse: {"response": "Default"}`).
767
+
768
+ `Fetch.continueRequest`, `continueResponse`, `failRequest`, `fulfillRequest`, `continueWithAuth` and `Network.continueInterceptedRequest` are refused with `slicc: refused: slicc didn't pass this paused request to the page, so it could be seven's` for any `requestId` or `interceptionId` the node hasn't passed to that session in a paused event. So a guessed id can't fulfil, change or fail a protected request, even one that is still paused. The node remembers the last 16 384 ids it passed on; an id older than that is refused too.
763
769
  - **Browser-wide commands**, refused with their own reason after `slicc: refused: `: `Target.exposeDevToolsProtocol` (it would give a page the whole browser) and `Target.sendMessageToTarget` (attach with `flatten: true` instead), on any target; `Fetch.enable` on the browser itself (a page session is fine); `Tracing.start`; `Extensions.loadUnpacked`; and `Browser.setPermission`, `Browser.grantPermissions` without an `origin` and `Browser.resetPermissions`, except in a browser context this stream created.
764
770
  - **Auto-attach.** With `Target.setAutoAttach`, a session to a protected target is detached by the node at once (`Target.detachFromTarget` on its parent session), and the page never sees its `attachedToTarget` or `detachedFromTarget`.
765
771
  - **Navigation by other means.** A script's `location = …`, a link, `window.open` or a server redirect isn't a command the node can refuse, so the node guards the network instead.
@@ -770,7 +776,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
770
776
  - Page commands on a touched target's session wait until the node's own session there is ready. That takes at most 10 s; after that, or when the node's session can't be set up or is lost, they are refused with `slicc: refused: slicc can't keep seven's origin out of this target`.
771
777
  - In touched targets, service workers are bypassed for every request, not only for protected ones.
772
778
  - The guard covers every frame that shares the touched target's process. A cross-site iframe's navigation pauses in its parent's target, before the iframe becomes a target of its own, with the frame's id as `frameId` (the id the iframe target gets later). A popup's request pauses in the popup's target, with its `targetId` as `frameId`. A redirect into a protected origin pauses again at the request stage, with `redirectedRequestId`, even when the first URL didn't match.
773
- - The page's own `Fetch.enable` on a page session still works. Chrome chains the interceptors of a target's sessions, the most recently enabled first, so the page's interceptor could see a protected document's request before the node's does, or after the node failed it, at the response stage. The node fails such a `Fetch.requestPaused` (`resourceType: Document`, protected URL, either stage) itself, and the page never sees it.
779
+ - The page's own `Fetch.enable` on a page session still works. Chrome chains the interceptors of a target's sessions, the most recently enabled first, so the page's interceptor could see a protected document's request before the node's does, or after the node failed it, at the response stage. The node fails such a request itself, and the page never sees it (see *Paused requests to seven* above).
774
780
  - When a target commits a protected origin all the same (see the limits below), the node sees it arrive (`Target.targetInfoChanged`) only after the browser committed it. A request the node failed also leaves the target on the protected URL, showing the error page. Either way the node detaches every page session on the target, drops their events and results, and refuses further commands on them; the page gets `Target.detachedFromTarget`.
775
781
  - **Auto-attach of targets without a URL.** A cross-site iframe can attach before its URL is known (`url: ""`, which stays empty while it waits for the debugger), and so can a `window.open` popup. So when an auto-attached session (one that doesn't answer the page's own `Target.attachToTarget`) is to a page or iframe target with an empty URL, the node holds it:
776
782
  - The page doesn't get its `attachedToTarget` yet, its events are dropped, and page commands on it are refused.
@@ -1189,7 +1195,17 @@ In the same process, probe ports, dirs and a process list passed in explicitly (
1189
1195
 
1190
1196
  **Port guard.** On CI (`CI` set), or with `SLICC_TEST_PORT_GUARD=1`, `TestMain` holds `127.0.0.1:9222` to `9229` (and `[::1]` where it can) for the whole run, and the run fails if anything connects to them, other than the one test that lends a port to its own fake browser. On CI a port it can't hold fails the run. Off CI it is off by default, so a developer's own Chrome on those ports is never touched; the guarded tests then skip.
1191
1197
 
1192
- On macOS, a headless Chrome started with `HOME` pointed at a temporary dir may never load a page (seen on a developer Mac, not on CI). So on macOS the test that has `slicc` launch Chrome for seven runs it with the real `HOME` and a unique `--profile`, which it deletes afterwards.
1198
+ **Protected-origin vectors.** `test/integration/testdata/link/protect.js` exports `protectVectors`, the attacks the relay must stop, in a shape other guards reuse: `{ id, setup, trigger, control: { origin: 'ordinary', expected }, expected }`. Each runs three times, through the relay, against the same server under three names: an ordinary origin (`ordinary.test`, the control, which must succeed), seven (an `--origin`) and the vault (`vault.sliccy.ai`). A run passes when its observations match `control.expected` or `expected`, and a vector whose control fails fails as a whole. The only run left out is the vault in the same-site iframe cases, since no unprotected page is same-site with a `sliccy.ai` host. The observations come from the test server's log of requests and of the beacons an injected init script sends, and from what the page's own CDP session saw. The cases:
1199
+ - C1, an init script, then `location.href`;
1200
+ - C2, redirect chains through allowed hops (301, 302, 303, 307, 308, and a POST form through 307 and 308), and a chain that passes through the protected origin and back out;
1201
+ - C3, an instrumented popup that navigates itself, and a popup opened on a redirect chain;
1202
+ - C4, static and script-inserted iframes, same-site and cross-site to the page;
1203
+ - C5, the page's own `Fetch.enable` on every request at both stages: a document, `fetch()`, XHR, an image, an auth challenge, and fulfilling guessed request ids;
1204
+ - C6, cookie reads, writes and clears, and storage commands that name the origin or its storage key.
1205
+
1206
+ Chrome 156 no longer has `Network.setRequestInterception`, so the deprecated `Network.requestIntercepted` path is tested against a fake browser instead.
1207
+
1208
+ **Real browsers in tests.** The Chrome the harness starts itself is `SLICC_TEST_CHROME` (test-only: the path of a Chrome executable), else the machine's Chrome or Chromium, with the real environment, a temporary profile and `--use-mock-keychain --password-store=basic`. The tests that have `slicc` itself launch a real Chrome run it under a temporary home, where its profiles are throwaway ones. Off CI they never launch the machine's own Chrome: they need `SLICC_TEST_CHROME` set to a Chrome for Testing inside its `.app`, which they link into the temporary home's `Applications` folder on macOS and launch by its id (`chrome-for-testing`), and they skip with a notice without it, or on other systems. On CI they launch the runner's Google Chrome. An older Chrome for Testing can fail the tests that read Chrome's own prefs.
1193
1209
 
1194
1210
  On GitHub's Windows runners the system drive `C:` is slow: with Chrome profiles there, Chrome sometimes waited seconds before a new tab's first request (up to past the 15 s page-load limit) or before exiting (`did not exit`). With them on `D:`, the runner's temp drive, those stalls weren't seen and the suite ran about a quarter faster. So CI points `TEMP` and `TMP` at the runner's temp dir, and the tests' temporary homes and Chrome profiles live there. When a test page doesn't load, the failure says where it stopped: the tab's URL and `readyState`, the resources it fetched, and every request the page server saw.
1195
1211
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc",
3
- "version": "1.39.1",
3
+ "version": "1.39.3",
4
4
  "description": "SLICC's command line for seven: links a machine's network, compute and browsers to seven over WebRTC",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,12 +18,12 @@
18
18
  "node": ">=18"
19
19
  },
20
20
  "optionalDependencies": {
21
- "@ai-ecoverse/slicc-darwin-arm64": "1.39.1",
22
- "@ai-ecoverse/slicc-darwin-amd64": "1.39.1",
23
- "@ai-ecoverse/slicc-linux-arm64": "1.39.1",
24
- "@ai-ecoverse/slicc-linux-amd64": "1.39.1",
25
- "@ai-ecoverse/slicc-windows-arm64": "1.39.1",
26
- "@ai-ecoverse/slicc-windows-amd64": "1.39.1",
27
- "@ai-ecoverse/slicc-jerk": "^0.15.2"
21
+ "@ai-ecoverse/slicc-darwin-arm64": "1.39.3",
22
+ "@ai-ecoverse/slicc-darwin-amd64": "1.39.3",
23
+ "@ai-ecoverse/slicc-linux-arm64": "1.39.3",
24
+ "@ai-ecoverse/slicc-linux-amd64": "1.39.3",
25
+ "@ai-ecoverse/slicc-windows-arm64": "1.39.3",
26
+ "@ai-ecoverse/slicc-windows-amd64": "1.39.3",
27
+ "@ai-ecoverse/slicc-jerk": "^0.16.0"
28
28
  }
29
29
  }