@ai-ecoverse/slicc 1.33.0 → 1.34.0
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.
- package/README.md +58 -10
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ A profile that doesn't exist is an error (`SLICC six's Chrome profile doesn't ex
|
|
|
140
140
|
Put the binary and the release's `SHA256SUMS` in one folder, then check:
|
|
141
141
|
- **Checksum on macOS and Linux:** `shasum -a 256 -c SHA256SUMS --ignore-missing`. On Linux `sha256sum -c SHA256SUMS --ignore-missing` does the same.
|
|
142
142
|
- **Checksum on Windows (PowerShell):** `(Get-FileHash slicc-windows-amd64.exe -Algorithm SHA256).Hash.ToLower()` must equal the hash on that file's line in `SHA256SUMS` (`Select-String slicc-windows-amd64.exe SHA256SUMS`).
|
|
143
|
-
- **Provenance:** `gh attestation verify <file> --repo ai-ecoverse/slicc-cli` checks the GitHub build-provenance attestation (Sigstore) that the release workflow made for that exact file.
|
|
143
|
+
- **Provenance:** `gh attestation verify <file> --repo ai-ecoverse/slicc-cli` checks the GitHub build-provenance attestation (Sigstore) that the release workflow made for that exact file. `slicc update` checks the same attestation itself (see [Attestations](#attestations)).
|
|
144
144
|
|
|
145
145
|
Signing:
|
|
146
146
|
- **macOS:** the binaries are signed with a Developer ID, with the hardened runtime, and notarized. A bare binary can't carry a stapled ticket (only apps, disk images and installer packages can), so Gatekeeper checks the notarization online the first time it runs. Inside Sliccstart.app the binary is stapled with the app.
|
|
@@ -153,17 +153,45 @@ slicc works with two tools that run on this machine: slicc-jerk (`jerk`), which
|
|
|
153
153
|
**With npm.** `@ai-ecoverse/slicc` and `sliccy` list `@ai-ecoverse/slicc-jerk` and `@ai-ecoverse/fnox` as `optionalDependencies` (`^` the latest release), but only those that are on the registry when the release is staged (`npm-stage.mjs --companions`); the others are left out with a notice. The publish check that waits for the platform packages doesn't wait for them. The launcher puts the `node_modules/.bin` of each installed companion package first on `PATH` for the binary, so `jerk` and `fnox` resolve for slicc and its children.
|
|
154
154
|
|
|
155
155
|
**`slicc install jerk|fnox`** installs one into the data folder's `bin`:
|
|
156
|
-
- `jerk` from the latest release of `ai-ecoverse/slicc-jerk
|
|
157
|
-
- **the release contract** (the same as slicc-cli's own): a plain binary `<name>-<os>-<arch>` (`.exe` on Windows; `darwin`, `linux`, `windows` × `amd64`, `arm64`), and `SHA256SUMS` with a line for it,
|
|
158
|
-
- slicc checks the download against `SHA256SUMS
|
|
159
|
-
- it says `installed <name> <version> at <path
|
|
156
|
+
- `jerk` from the latest release of `ai-ecoverse/slicc-jerk` (0.3.0 or later; an older latest release is refused with `… predates the release layout slicc installs from`), `fnox` from the latest release of `ai-ecoverse/fnox-build`;
|
|
157
|
+
- **the release contract** (the same as slicc-cli's own): a plain binary `<name>-<os>-<arch>` (`.exe` on Windows; `darwin`, `linux`, `windows` × `amd64`, `arm64`), and `SHA256SUMS` with a line for it, with a build provenance attestation by the repo's `release.yml` over the binary or over `SHA256SUMS`;
|
|
158
|
+
- slicc checks the download against `SHA256SUMS` and verifies its attestation (see [Attestations](#attestations)), then installs it only once its `--version` names the release's version, through a rename next to the old one (as `slicc update` does);
|
|
159
|
+
- it says `installed <name> <version> at <path> (SHA256SUMS and the build provenance attestation by <identity>)`, or `<name> <version> is already installed at <path>`;
|
|
160
|
+
- `--checksum-only` skips the attestation, with a warning, and says `SHA256SUMS only; the attestation wasn't checked`;
|
|
160
161
|
- the GitHub API base follows `SLICC_UPDATE_API_BASE`, as for `update`.
|
|
161
162
|
|
|
162
163
|
Only a user runs `slicc install`. Nothing on a link starts an install.
|
|
163
164
|
|
|
165
|
+
## Attestations
|
|
166
|
+
|
|
167
|
+
`slicc update` and `slicc install` verify the GitHub build provenance attestation of every binary they download, after its `SHA256SUMS` check, and refuse it when that fails. A release without an attestation fails too. `--checksum-only` is the only way around it, and it warns.
|
|
168
|
+
|
|
169
|
+
**Where the attestation comes from.** `GET /repos/<repo>/attestations/sha256:<digest>` on the GitHub API, first for the binary's digest. When the binary has none, slicc looks up the digest of the release's `SHA256SUMS` instead: an attestation over `SHA256SUMS` covers the binary through its line there, which slicc has already checked. slicc accepts the binary when one of the bundles verifies.
|
|
170
|
+
|
|
171
|
+
**What is verified** (`internal/attest`, standard library only), for a Sigstore bundle v0.3 with one DSSE envelope, one Fulcio certificate and one Rekor (v1) entry:
|
|
172
|
+
1. the envelope is an in-toto statement whose predicate type is `https://slsa.dev/provenance/v1`, with a subject whose SHA-256 is the file's;
|
|
173
|
+
2. its single DSSE signature verifies with the certificate's key (ECDSA P-256 or P-384) over the DSSE pre-authentication encoding;
|
|
174
|
+
3. the certificate chains, as a code-signing certificate, to a Fulcio root in the trusted root that is valid at the entry's integrated time, and the certificate itself was valid then;
|
|
175
|
+
4. one of the certificate's embedded SCTs (`1.3.6.1.4.1.11129.2.4.2`) verifies with a certificate transparency log key in the trusted root, valid at the SCT's time, over the precertificate (the certificate body without its SCT list) and its issuer's key hash (RFC 6962);
|
|
176
|
+
5. the certificate's identity: issuer (Fulcio extension `1.3.6.1.4.1.57264.1.8`) `https://token.actions.githubusercontent.com`, and exactly one subject alternative name, the URI `https://github.com/<repo>/.github/workflows/release.yml@refs/heads/main`, pinned per repo: `ai-ecoverse/slicc-cli` for `update`, `ai-ecoverse/slicc-jerk` and `ai-ecoverse/fnox-build` for `install`;
|
|
177
|
+
6. the Rekor entry is a `dsse` 0.0.1 entry whose payload hash, signature and verifier are the envelope's and the certificate's;
|
|
178
|
+
7. its signed entry timestamp verifies with the key of a Rekor log in the trusted root, valid at the integrated time;
|
|
179
|
+
8. its inclusion proof leads from the entry to the proof's root hash (RFC 6962), and the checkpoint carries that tree size and root hash and is signed by the same log.
|
|
180
|
+
|
|
181
|
+
Errors name the step that failed (`attestation identity: …`). A bundle from a Rekor v2 log, or any other shape, fails closed.
|
|
182
|
+
|
|
183
|
+
**The trusted root.** `internal/attest/trusted_root.json` is Sigstore's public-good trusted root (Fulcio CAs, Rekor and CT log keys) (the `trusted_root.json` target of `https://tuf-repo-cdn.sigstore.dev`), embedded in the binary. `SLICC_SIGSTORE_TRUSTED_ROOT` replaces it, for tests.
|
|
184
|
+
|
|
185
|
+
**Rotating the trusted root.** The *Sigstore* workflow runs daily. Its `trusted-root` job runs `node scripts/sigstore-root.mjs`, which follows the TUF repository's `timestamp.json`, snapshot and targets to the current `trusted_root.json` and compares its SHA-256 with the embedded file; when they differ, it opens an issue. To rotate:
|
|
186
|
+
1. run `node scripts/sigstore-root.mjs --write`, which downloads the new file and checks it against the targets hash;
|
|
187
|
+
2. review the diff (new Fulcio certificates or Rekor keys, `validFor` ranges) against Sigstore's announcement; the script doesn't check TUF signatures, so this review is the trust decision;
|
|
188
|
+
3. open a `fix:` PR, so a release ships it. Binaries released before it keep the old root, and fail closed on attestations that need the new one until they update with `--checksum-only` or by hand.
|
|
189
|
+
|
|
190
|
+
**Parity.** The workflow's `parity` job runs `TestAttestParityWithGH`: for the latest release, `gh attestation verify --repo ai-ecoverse/slicc-cli --signer-workflow …/release.yml` and slicc's verifier must both accept the binary, and both refuse it after one byte is appended.
|
|
191
|
+
|
|
164
192
|
## Updates
|
|
165
193
|
|
|
166
|
-
`slicc update` replaces the running binary (symlinks resolved) with the latest GitHub release of `ai-ecoverse/slicc-cli`. It downloads the asset for this OS and CPU
|
|
194
|
+
`slicc update [--check] [--checksum-only]` replaces the running binary (symlinks resolved) with the latest GitHub release of `ai-ecoverse/slicc-cli`. It downloads the asset for this OS and CPU, checks it against the release's `SHA256SUMS`, and verifies its build provenance attestation (see [Attestations](#attestations)); `--checksum-only` skips the attestation, with a warning. The new binary has to answer `--version` with the release's version before anything is replaced. The swap is a rename next to the old binary; on Windows the running binary is first moved aside to `<name>.old`, which the next run deletes. `slicc update --check` only reports.
|
|
167
195
|
|
|
168
196
|
**The version.** A release build has its version linked in (`-X main.version=<version>`). Without one, `slicc` reads its Go build info: a binary built by `go install …@<version>` (or `go run`) from a downloaded module, with a checksum in the build info, reports that module version without the leading `v`. Anything else built from a checkout reports `dev`.
|
|
169
197
|
|
|
@@ -191,7 +219,8 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
|
|
|
191
219
|
| --- | --- |
|
|
192
220
|
| `SLICC_NO_TELEMETRY=1` | send no telemetry |
|
|
193
221
|
| `SLICC_NO_UPDATE_CHECK=1` | no update notice and no background check |
|
|
194
|
-
| `SLICC_UPDATE_API_BASE` | GitHub API base for `update` and the notice (default `https://api.github.com`), for mirrors and tests |
|
|
222
|
+
| `SLICC_UPDATE_API_BASE` | GitHub API base for `update`, `install` and the notice (default `https://api.github.com`), for mirrors and tests |
|
|
223
|
+
| `SLICC_SIGSTORE_TRUSTED_ROOT` | a Sigstore `trusted_root.json` to verify attestations against instead of the embedded one, for tests (see [Attestations](#attestations)) |
|
|
195
224
|
| `SLICC_TELEMETRY_URL` | telemetry collector base (default `https://rum.hlx.page/`) |
|
|
196
225
|
| `OPTEL_RATE`, `OPTEL_DEBUG` | go-optel's sampling rate and beacon logging |
|
|
197
226
|
| `SLICC_LAUNCHER` | set to `npm` by the npm launcher; removed from the environment that `slicc` passes on |
|
|
@@ -418,6 +447,7 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
|
|
|
418
447
|
- Each offer starts a new link and replaces the current one.
|
|
419
448
|
- A navigation or reload ends the link, and the next allowed context starts the setup again with a new nonce.
|
|
420
449
|
- A closed tab ends the link, and slicc waits for another seven tab.
|
|
450
|
+
- A tab that closes while slicc attaches to it doesn't stop slicc: each setup call has 10 s, and when the tab is gone slicc looks for another seven tab.
|
|
421
451
|
|
|
422
452
|
**Setup over stdio** (`--signal stdio`), for a test harness that owns CDP itself:
|
|
423
453
|
- The same messages go as NDJSON: slicc writes `hello`, `answer`, `candidate` and `bye` lines to stdout, and reads `offer`, `candidate` and `bye` lines from stdin. Messages with another nonce are ignored.
|
|
@@ -517,12 +547,30 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
|
|
|
517
547
|
- **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.
|
|
518
548
|
- **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.
|
|
519
549
|
- **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`.
|
|
520
|
-
- **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. 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. Continuing, failing or fulfilling it is allowed.
|
|
550
|
+
- **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. 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. Continuing, failing or fulfilling it is allowed, except for a document request, which the node fails itself (see the next items).
|
|
521
551
|
- **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.
|
|
522
552
|
- **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`.
|
|
523
|
-
- **Navigation by other means.** A script's `location = …`, a link, `window.open` or a server redirect
|
|
553
|
+
- **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.
|
|
554
|
+
- A target the stream attached to (`Target.attachToTarget`, or auto-attach that reached the page) or created (`Target.createTarget`) is *touched*, for as long as the stream is open, even after the page detached. That includes every tab the browser opens while the stream auto-attaches to new tabs.
|
|
555
|
+
- On each touched page, iframe, background page or webview target the node attaches a session of its own, which the page never sees. On it, the node enables `Fetch` for document requests to protected hosts (`resourceType: Document`, request stage), and `Network` with `Network.setBypassServiceWorker`, so a service worker can't hand out a document without a request.
|
|
556
|
+
- The node fails each such request into a protected origin with `Fetch.failRequest` (`BlockedByClient`), and the target shows Chrome's error page instead. Every other request continues untouched. So a touched target never loads a protected document, and nothing the stream installed on it (`Page.addScriptToEvaluateOnNewDocument`, `Fetch`, `Network`, `Page.setBypassCSP`, emulation, overrides) ever runs there.
|
|
557
|
+
- Seven's own tab and its vault frame are never touched, because attaching to them is refused.
|
|
558
|
+
- 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`.
|
|
559
|
+
- In touched targets, service workers are bypassed for every request, not only for protected ones.
|
|
560
|
+
- 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.
|
|
561
|
+
- 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.
|
|
562
|
+
- 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`.
|
|
563
|
+
- **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:
|
|
564
|
+
- The page doesn't get its `attachedToTarget` yet, its events are dropped, and page commands on it are refused.
|
|
565
|
+
- If the page asked for `waitForDebuggerOnStart`, the node runs `Runtime.runIfWaitingForDebugger` on it itself.
|
|
566
|
+
- Once the target has a URL (`Target.targetInfoChanged`), a protected target's session is detached without the page seeing it. Otherwise the page gets `attachedToTarget` with the current `targetInfo` and `waitingForDebugger: false`. The target has already started, so a script the page adds for new documents in such a frame or popup misses its first one.
|
|
567
|
+
- A target that has no URL after 10 s is treated as protected. A session that the node does forward makes the target touched (see above).
|
|
524
568
|
- **Listing.** `Target.getTargets`, `Target.getTargetInfo`, `Target.targetCreated` and `Target.targetInfoChanged` still show protected targets, so seven knows its tab exists, but their `url` and `title` are only the origin (`https://seven.sliccy.ai`, or `about:blank`). Service worker registrations and versions on a protected origin are left out of `ServiceWorker` events, and `Storage`, `DOMStorage` and `BackgroundService` events that name a protected origin are dropped.
|
|
525
|
-
- **Limits.**
|
|
569
|
+
- **Limits.**
|
|
570
|
+
- The filter relies on Chrome's site isolation. A seven frame inside a page of another site is a target of its own (`iframe`), which the node protects. A page the stream never touched can still frame seven, though, and once the stream touches that page, a screenshot of it shows the frame. A same-site frame shares its parent's process and isn't a target. Every `sliccy.ai` host is protected, so this only matters for an extra origin, such as one port of `127.0.0.1` framed by a page on another.
|
|
571
|
+
- A document that arrives without a request isn't failed: a page restored from the back/forward cache, and a `blob:` or `filesystem:` URL of a protected origin (which only that origin can make). The node detaches after the commit, as above, and commands that were already on their way may still have run in the new page.
|
|
572
|
+
- If the node loses its own session (`ECONNRESET: slicc lost track of the browser's targets`), it refuses every command and resets the stream unless the browser closes within 2 s.
|
|
573
|
+
- The node's own setup connection (see [Local setup](#local-setup-slicc-attach)) is not a `cdp` stream and isn't filtered.
|
|
526
574
|
|
|
527
575
|
**List changes.** The node checks every listed browser's debugging port every 2 s. One that fails twice in a row is removed: its open streams get `RESET ECONNRESET: browser quit`, and a later OPEN for it gets `ENOENT`. Once its port answers again it is listed again under the same id. A browser removed through the API (`Registry.Remove`, for the TUI) has its streams reset with `ECONNRESET: browser removed`.
|
|
528
576
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
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,13 @@
|
|
|
18
18
|
"node": ">=18"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@ai-ecoverse/slicc-darwin-arm64": "1.
|
|
22
|
-
"@ai-ecoverse/slicc-darwin-amd64": "1.
|
|
23
|
-
"@ai-ecoverse/slicc-linux-arm64": "1.
|
|
24
|
-
"@ai-ecoverse/slicc-linux-amd64": "1.
|
|
25
|
-
"@ai-ecoverse/slicc-windows-arm64": "1.
|
|
26
|
-
"@ai-ecoverse/slicc-windows-amd64": "1.
|
|
27
|
-
"@ai-ecoverse/slicc-jerk": "^0.
|
|
21
|
+
"@ai-ecoverse/slicc-darwin-arm64": "1.34.0",
|
|
22
|
+
"@ai-ecoverse/slicc-darwin-amd64": "1.34.0",
|
|
23
|
+
"@ai-ecoverse/slicc-linux-arm64": "1.34.0",
|
|
24
|
+
"@ai-ecoverse/slicc-linux-amd64": "1.34.0",
|
|
25
|
+
"@ai-ecoverse/slicc-windows-arm64": "1.34.0",
|
|
26
|
+
"@ai-ecoverse/slicc-windows-amd64": "1.34.0",
|
|
27
|
+
"@ai-ecoverse/slicc-jerk": "^0.5.0",
|
|
28
|
+
"@ai-ecoverse/fnox": "^0.0.0"
|
|
28
29
|
}
|
|
29
30
|
}
|