@hraness/ghostget 0.17.2
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/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +1359 -0
- package/bunfig.toml +2 -0
- package/dist/apple-photos-client.js +704 -0
- package/dist/beeper-client.js +4602 -0
- package/dist/client.js +1605 -0
- package/dist/index-26yq8q16.js +31 -0
- package/dist/index-9t13b4k7.js +508 -0
- package/dist/index-axwm9b3v.js +5 -0
- package/dist/index-gwk7rbyj.js +30 -0
- package/dist/index.js +701 -0
- package/dist/messaging.js +1130 -0
- package/dist/omni-client.js +1406 -0
- package/dist/whatsapp-client.js +355 -0
- package/docs/imessage-direct-provider.md +106 -0
- package/docs/rental-listings.md +94 -0
- package/package.json +600 -0
- package/skills/ghostget/SKILL.md +251 -0
- package/skills/ghostget/agents/openai.yaml +4 -0
- package/skills/ghostget/references/adapter-contract.md +181 -0
- package/skills/ghostget/references/apple-photos.md +115 -0
- package/skills/ghostget/references/article-drafts.md +235 -0
- package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
- package/skills/ghostget/references/cross-posting-video.md +139 -0
- package/skills/ghostget/references/cross-posting.md +82 -0
- package/skills/ghostget/references/derivation.md +390 -0
- package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
- package/skills/ghostget/references/install.md +66 -0
- package/skills/ghostget/references/linkedin-adapter.md +442 -0
- package/skills/ghostget/references/messaging.md +168 -0
- package/skills/ghostget/references/meta-comet-contract.md +153 -0
- package/skills/ghostget/references/platform-patterns.md +122 -0
- package/skills/ghostget/references/provider-plugins.md +496 -0
- package/skills/ghostget/references/reddit-flair.md +78 -0
- package/skills/ghostget/references/safety-and-state.md +214 -0
- package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
- package/skills/ghostget/references/social-platform-routing.md +66 -0
- package/skills/ghostget/references/social-profile-stats.md +195 -0
- package/skills/ghostget/references/social-video-platform-routing.md +89 -0
- package/skills/ghostget/references/whatsapp-adapter.md +101 -0
- package/skills/ghostget/references/x-adapter.md +332 -0
- package/skills/ghostget/references/x-ai-disclosure.md +71 -0
- package/src/apple-photos-cli.ts +639 -0
- package/src/apple-photos-client-types.ts +168 -0
- package/src/apple-photos-client.ts +201 -0
- package/src/apple-photos-contact-evidence.ts +838 -0
- package/src/apple-photos-local-source.ts +1131 -0
- package/src/args.ts +2053 -0
- package/src/article-draft-document.ts +417 -0
- package/src/article-draft-embeds.ts +123 -0
- package/src/article-draft-images.ts +214 -0
- package/src/assets/adapter-template/wrench-adapter.json +40 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
- package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
- package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
- package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
- package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
- package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
- package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
- package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
- package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
- package/src/assets/adapters/x/wrench-adapter.json +749 -0
- package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
- package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
- package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
- package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
- package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
- package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
- package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
- package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/assets.d.ts +4 -0
- package/src/auth.ts +1157 -0
- package/src/beeper-client-types.ts +169 -0
- package/src/beeper-client.ts +250 -0
- package/src/beeper-contact-interactions-cli.ts +180 -0
- package/src/beeper-contact-interactions.ts +1612 -0
- package/src/beeper-message-bundle-v1.ts +257 -0
- package/src/beeper-message-like-me-cli.ts +84 -0
- package/src/beeper-message-like-me-export.ts +2274 -0
- package/src/beeper-message-like-me-recovery.ts +931 -0
- package/src/beeper-message-like-me-source.ts +4034 -0
- package/src/browser-admission.ts +928 -0
- package/src/browser-snapshots.ts +260 -0
- package/src/browser.ts +3873 -0
- package/src/canonical-json.ts +60 -0
- package/src/catalog-cli.ts +638 -0
- package/src/cli.ts +208 -0
- package/src/client-types.ts +301 -0
- package/src/client.ts +2726 -0
- package/src/confirmed-write-failure.ts +44 -0
- package/src/confirmed-write-model.ts +72 -0
- package/src/confirmed-write-platform.ts +1055 -0
- package/src/confirmed-write-program.ts +224 -0
- package/src/confirmed-write-runtime.ts +16 -0
- package/src/cursor-token.ts +228 -0
- package/src/derivation-dnr-readiness.ts +367 -0
- package/src/derivation-file-chooser.ts +384 -0
- package/src/derivation-network-boundary.ts +835 -0
- package/src/derivation-network-guard.ts +782 -0
- package/src/derivation-network-proxy-helper.ts +440 -0
- package/src/derivation-network-proxy.ts +385 -0
- package/src/derive-command-helper.ts +693 -0
- package/src/derive-fixtures.ts +343 -0
- package/src/derive-review.ts +980 -0
- package/src/derive.ts +4754 -0
- package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
- package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
- package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
- package/src/ghostget.ts +3672 -0
- package/src/gmail-capture.ts +1244 -0
- package/src/har-internal.ts +1242 -0
- package/src/har.ts +743 -0
- package/src/index.ts +218 -0
- package/src/invocation-read-platform.ts +55 -0
- package/src/invocation-read-program.ts +93 -0
- package/src/invocation-read-runtime.ts +11 -0
- package/src/linked-device-lifecycle-admission.ts +450 -0
- package/src/linked-device-lifecycle-journal.ts +1560 -0
- package/src/linked-device-lifecycle-runtime.ts +1602 -0
- package/src/local-cli-admission.ts +135 -0
- package/src/local-cli-contracts.ts +271 -0
- package/src/local-cli-execution.ts +98 -0
- package/src/local-cli-surface-contract.ts +1510 -0
- package/src/local-cli-tool-identity.ts +336 -0
- package/src/media/archive.ts +2325 -0
- package/src/media/args.ts +303 -0
- package/src/media/cli.ts +287 -0
- package/src/media/doctor.ts +475 -0
- package/src/media/ffmpeg.ts +1330 -0
- package/src/media/http-capture.ts +711 -0
- package/src/media/http-probe.ts +593 -0
- package/src/media/http.ts +387 -0
- package/src/media/index.ts +10 -0
- package/src/media/local-transcription.ts +613 -0
- package/src/media/lock.ts +321 -0
- package/src/media/manifest.ts +1712 -0
- package/src/media/metadata.ts +906 -0
- package/src/media/process.ts +723 -0
- package/src/media/revision.ts +166 -0
- package/src/media/runtime-closure.ts +1063 -0
- package/src/media/source-router.ts +187 -0
- package/src/media/transcriber-config.ts +1122 -0
- package/src/media/transcript-persistence-model.ts +15 -0
- package/src/media/transcript-persistence-platform.ts +89 -0
- package/src/media/transcript-persistence-program.ts +39 -0
- package/src/media/transcript-persistence-runtime.ts +24 -0
- package/src/media/transcript.ts +616 -0
- package/src/media/utf8-order.ts +30 -0
- package/src/media/whisper-cpp.ts +464 -0
- package/src/media/whisper-language.ts +58 -0
- package/src/media/yt-dlp.ts +439 -0
- package/src/message-like-me-agentic-messaging.ts +882 -0
- package/src/messaging-action-store.ts +793 -0
- package/src/messaging-runtime.ts +2136 -0
- package/src/messaging-store.ts +539 -0
- package/src/messaging-types.ts +1663 -0
- package/src/messaging.ts +334 -0
- package/src/model.ts +2671 -0
- package/src/oauth-google.ts +888 -0
- package/src/omni-client-types.ts +306 -0
- package/src/omni-client.ts +2225 -0
- package/src/omni-limits.ts +34 -0
- package/src/omni-model.ts +1614 -0
- package/src/omni-request.ts +379 -0
- package/src/omni-runtime.ts +1632 -0
- package/src/operation-deadline.ts +189 -0
- package/src/path-helper.ts +1656 -0
- package/src/pinned-https.ts +194 -0
- package/src/plan-assets.ts +832 -0
- package/src/platform-catalog.ts +1832 -0
- package/src/plugins/beeper-linked-device/plugin.ts +310 -0
- package/src/plugins/bluesky-web/plugin.ts +178 -0
- package/src/plugins/clasificados-web/plugin.ts +66 -0
- package/src/plugins/github-web/plugin.ts +80 -0
- package/src/plugins/gmail-official/plugin.ts +110 -0
- package/src/plugins/hacker-news-web/plugin.ts +52 -0
- package/src/plugins/imessage-direct/plugin.ts +195 -0
- package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
- package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
- package/src/plugins/linkedin-official/plugin.ts +84 -0
- package/src/plugins/linkedin-web/plugin.ts +412 -0
- package/src/plugins/meta-web/plugin.ts +317 -0
- package/src/plugins/reddit-web/flair.ts +492 -0
- package/src/plugins/reddit-web/plugin.ts +477 -0
- package/src/plugins/substack-web/plugin.ts +124 -0
- package/src/plugins/tiktok-web/plugin.ts +67 -0
- package/src/plugins/twitch-web/plugin.ts +54 -0
- package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
- package/src/plugins/x-official/plugin.ts +64 -0
- package/src/plugins/x-web/plugin.ts +488 -0
- package/src/plugins/youtube-web/plugin.ts +101 -0
- package/src/portable-run-recovery.ts +466 -0
- package/src/process-identity.ts +501 -0
- package/src/profile-clone-helper.ts +104 -0
- package/src/provider-catalog-views.ts +51 -0
- package/src/provider-context.ts +69 -0
- package/src/provider-contract-definitions-x.ts +45 -0
- package/src/provider-contract-definitions.ts +948 -0
- package/src/provider-contract-input-gmail.ts +176 -0
- package/src/provider-contract-input-linkedin.ts +77 -0
- package/src/provider-contract-input-x.ts +304 -0
- package/src/provider-contract-planning.ts +23 -0
- package/src/provider-contract-semantic-identity.ts +54 -0
- package/src/provider-contracts.ts +251 -0
- package/src/provider-http.ts +723 -0
- package/src/provider-plugin-auth.ts +36 -0
- package/src/provider-plugin-builtins.ts +229 -0
- package/src/provider-plugin-cleanup-barrier.ts +229 -0
- package/src/provider-plugin-cleanup-execution.ts +79 -0
- package/src/provider-plugin-cleanup-resource.ts +470 -0
- package/src/provider-plugin-contract-identity.ts +707 -0
- package/src/provider-plugin-host.ts +1972 -0
- package/src/provider-plugin-identifiers.ts +60 -0
- package/src/provider-plugin-invocation-lease.ts +889 -0
- package/src/provider-plugin-lifecycle-kernel.ts +566 -0
- package/src/provider-plugin-lifecycle.ts +1455 -0
- package/src/provider-plugin-module-analysis.ts +336 -0
- package/src/provider-plugin-package.ts +2530 -0
- package/src/provider-plugin-portable-authority.ts +106 -0
- package/src/provider-plugin-portable-catalog.ts +94 -0
- package/src/provider-plugin-portable-identity.ts +315 -0
- package/src/provider-plugin-portable-runtime.ts +2057 -0
- package/src/provider-plugin-protocol.ts +1625 -0
- package/src/provider-plugin-registry.ts +3342 -0
- package/src/provider-plugin-store.ts +2155 -0
- package/src/provider-plugin-test-fixture.ts +5 -0
- package/src/provider-plugin.ts +4907 -0
- package/src/provider-plugins.generated.ts +43 -0
- package/src/provider-plugins.ts +8 -0
- package/src/provider-subject.ts +41 -0
- package/src/provider.ts +315 -0
- package/src/providers/beeper-local-runtime.ts +5426 -0
- package/src/providers/beeper-local.ts +2739 -0
- package/src/providers/beeper-messaging.ts +462 -0
- package/src/providers/beeper-omni.ts +1159 -0
- package/src/providers/bluesky-web-runtime.ts +2895 -0
- package/src/providers/bluesky-web.ts +1614 -0
- package/src/providers/clasificados-web-runtime.ts +255 -0
- package/src/providers/clasificados-web.ts +503 -0
- package/src/providers/contact-projection.ts +301 -0
- package/src/providers/github-read-model.ts +94 -0
- package/src/providers/github-read-platform.ts +165 -0
- package/src/providers/github-read-program.ts +97 -0
- package/src/providers/github-web-runtime.ts +320 -0
- package/src/providers/github-web.ts +403 -0
- package/src/providers/gmail-api.ts +2834 -0
- package/src/providers/gmail-omni.ts +830 -0
- package/src/providers/gmail.ts +1130 -0
- package/src/providers/hacker-news-web-runtime.ts +220 -0
- package/src/providers/hacker-news-web.ts +809 -0
- package/src/providers/imessage-direct-install.ts +354 -0
- package/src/providers/imessage-direct-messaging.ts +532 -0
- package/src/providers/imessage-direct-omni.ts +400 -0
- package/src/providers/imessage-direct-runtime.ts +1498 -0
- package/src/providers/imessage-direct.ts +351 -0
- package/src/providers/instagram-video-foundations.ts +913 -0
- package/src/providers/instagram-web-profile-browser.ts +600 -0
- package/src/providers/iso-bmff.ts +338 -0
- package/src/providers/linkedin-company-platform.ts +42 -0
- package/src/providers/linkedin-company-program.ts +88 -0
- package/src/providers/linkedin-contact-failure.ts +84 -0
- package/src/providers/linkedin-contact-platform.ts +45 -0
- package/src/providers/linkedin-contact-program.ts +91 -0
- package/src/providers/linkedin-profile-activity-failure.ts +76 -0
- package/src/providers/linkedin-profile-activity-platform.ts +41 -0
- package/src/providers/linkedin-profile-activity-program.ts +67 -0
- package/src/providers/linkedin-read-failure.ts +52 -0
- package/src/providers/linkedin-self-platform.ts +53 -0
- package/src/providers/linkedin-self-program.ts +122 -0
- package/src/providers/linkedin-web-article-browser.ts +794 -0
- package/src/providers/linkedin-web-bootstrap.ts +146 -0
- package/src/providers/linkedin-web-contact.ts +830 -0
- package/src/providers/linkedin-web-feed-browser.ts +832 -0
- package/src/providers/linkedin-web-feed.ts +864 -0
- package/src/providers/linkedin-web-post-browser.ts +653 -0
- package/src/providers/linkedin-web-profile-browser.ts +683 -0
- package/src/providers/linkedin-web-runtime.ts +2388 -0
- package/src/providers/linkedin-web.ts +3305 -0
- package/src/providers/linkedin.ts +1885 -0
- package/src/providers/meta-bootstrap.ts +1043 -0
- package/src/providers/meta-facebook-group.ts +690 -0
- package/src/providers/meta-marketplace-relay.ts +747 -0
- package/src/providers/meta-omni.ts +373 -0
- package/src/providers/meta-relay-bundle.ts +1049 -0
- package/src/providers/meta-web-descriptors.ts +1740 -0
- package/src/providers/meta-web-runtime.ts +3388 -0
- package/src/providers/meta-web.ts +2931 -0
- package/src/providers/read-failure.ts +139 -0
- package/src/providers/reddit-omni.ts +462 -0
- package/src/providers/reddit-web-runtime.ts +1929 -0
- package/src/providers/reddit-web.ts +1611 -0
- package/src/providers/rental-listings.ts +460 -0
- package/src/providers/substack-omni.ts +406 -0
- package/src/providers/substack-video-mp4.ts +33 -0
- package/src/providers/substack-web-runtime.ts +2812 -0
- package/src/providers/substack-web.ts +1502 -0
- package/src/providers/tiktok-video-mp4.ts +33 -0
- package/src/providers/tiktok-web-runtime.ts +770 -0
- package/src/providers/tiktok-web.ts +2786 -0
- package/src/providers/twitch-web-runtime.ts +226 -0
- package/src/providers/twitch-web.ts +274 -0
- package/src/providers/whatsapp-account-identity.ts +55 -0
- package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
- package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
- package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
- package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
- package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
- package/src/providers/whatsapp-messaging.ts +132 -0
- package/src/providers/whatsapp-omni.ts +586 -0
- package/src/providers/whatsapp-web-runtime.ts +3108 -0
- package/src/providers/whatsapp-web.ts +1037 -0
- package/src/providers/x-image-provenance.ts +523 -0
- package/src/providers/x-made-with-ai.ts +106 -0
- package/src/providers/x-transaction-id.ts +413 -0
- package/src/providers/x-web-runtime.ts +3485 -0
- package/src/providers/x-web.ts +2332 -0
- package/src/providers/x.ts +1889 -0
- package/src/providers/youtube-web-runtime.ts +1400 -0
- package/src/providers/youtube-web.ts +1221 -0
- package/src/read-admission-runtime.ts +91 -0
- package/src/read-client.ts +521 -0
- package/src/read-effect-platform.ts +7 -0
- package/src/read-effect-runtime.ts +14 -0
- package/src/read-effect.ts +52 -0
- package/src/read-projection-admission.ts +866 -0
- package/src/read-projections.ts +3151 -0
- package/src/recovery.ts +1492 -0
- package/src/reviewed-template.ts +146 -0
- package/src/run-journal.ts +1272 -0
- package/src/runtime.ts +5116 -0
- package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
- package/src/scripts/install-whatsapp-protocol.sh +232 -0
- package/src/scripts/recover-install-stages.ts +1491 -0
- package/src/scripts/resolve-state-home.ts +30 -0
- package/src/scripts/scaffold-web-provider.ts +973 -0
- package/src/scripts/sync-bundled-adapters.ts +695 -0
- package/src/session-secrets.ts +1183 -0
- package/src/state-helper.bunfig.toml +2 -0
- package/src/state-helper.ts +2914 -0
- package/src/storage.ts +3842 -0
- package/src/transport-policy.ts +2 -0
- package/src/usage.ts +182 -0
- package/src/version.ts +2 -0
- package/src/web-session-authentication-policy.ts +136 -0
- package/src/web-session-cleanup-admission.ts +2344 -0
- package/src/web-session-cleanup-join.ts +99 -0
- package/src/web-session-client.ts +996 -0
- package/src/web-session-contract-definitions.ts +820 -0
- package/src/web-session-contract-planning.ts +42 -0
- package/src/web-session-contracts.ts +228 -0
- package/src/web-session-cookies.ts +500 -0
- package/src/web-session-execution.ts +385 -0
- package/src/web-session-http.ts +373 -0
- package/src/web-session-read-errors.ts +19 -0
- package/src/web-session-read-runtime.ts +124 -0
- package/src/web-session-recovery.ts +818 -0
- package/src/web-session-sites.ts +58 -0
- package/src/web-session-template.ts +994 -0
- package/src/web-session.ts +93 -0
- package/src/whatsapp-client-binding.ts +21 -0
- package/src/whatsapp-client-types.ts +75 -0
- package/src/whatsapp-client.ts +395 -0
- package/src/whatsapp-export-coordinate.ts +17 -0
- package/src/whatsapp-message-bundle-v2.ts +195 -0
- package/src/whatsapp-message-like-me-cli.ts +195 -0
- package/src/whatsapp-message-like-me-export.ts +1072 -0
- package/src/whatsapp-message-like-me-source.ts +1012 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
closeSync,
|
|
5
|
+
constants,
|
|
6
|
+
fchmodSync,
|
|
7
|
+
fstatSync,
|
|
8
|
+
fsyncSync,
|
|
9
|
+
lstatSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
openSync,
|
|
12
|
+
readSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
writeSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
|
|
19
|
+
import { canonicalJson, sha256 } from "./canonical-json";
|
|
20
|
+
import type { ProcessOwnerIdentity } from "./process-identity";
|
|
21
|
+
|
|
22
|
+
export const DERIVATION_GUARD_EXTENSION_DIRECTORY = "network-guard-extension";
|
|
23
|
+
export const DERIVATION_GUARD_PROXY_CONFIG = "network-proxy.json";
|
|
24
|
+
export const DERIVATION_GUARD_PROXY_READY = "network-proxy-ready.json";
|
|
25
|
+
/** Legacy schema-v1 location inside AGENT_BROWSER_SOCKET_DIR. */
|
|
26
|
+
export const DERIVATION_GUARD_CONTROL_SOCKET = "network-proxy-control.sock";
|
|
27
|
+
|
|
28
|
+
export const derivationGuardResourceTypes = Object.freeze([
|
|
29
|
+
"main_frame",
|
|
30
|
+
"sub_frame",
|
|
31
|
+
"stylesheet",
|
|
32
|
+
"script",
|
|
33
|
+
"image",
|
|
34
|
+
"font",
|
|
35
|
+
"object",
|
|
36
|
+
"xmlhttprequest",
|
|
37
|
+
"ping",
|
|
38
|
+
"csp_report",
|
|
39
|
+
"media",
|
|
40
|
+
"websocket",
|
|
41
|
+
"webtransport",
|
|
42
|
+
"webbundle",
|
|
43
|
+
"other",
|
|
44
|
+
] as const);
|
|
45
|
+
|
|
46
|
+
const extensionPublicKey =
|
|
47
|
+
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8IQKI/uEK3ld2F8VY/ijbVaaT9uIILnZWiqOpI8K/n6/I6RKAm+QgBDjfq+mnvArr4uIFdkNm60utfLZ+bc6iFs3W62og1O7kTqohzkWc8VLwCIXJ4rMfQN2GFh+CSYu122uteGau87T1iUZtIc+k5oocpXiFlKDO39ZacV5v2zZLwu6ifz8r3XmWo1Knhhez0TzlsMpgHY6O7oQb3UFzjU4loZo+LpBTzObov05czdXAVHoVWE7rdRUxNeVHQrla0oY/MBh87bpd2rrEqG8K/RgkXE3LOZrdGRK2mqYNQC0eJgwIRxnpyD7QxAifs2r1KAPQeCjmgkscAR6T8m6gwIDAQAB";
|
|
48
|
+
export const DERIVATION_GUARD_EXTENSION_ID = "gjhalpeeegljfdmfkoilmojkfehhpgbm";
|
|
49
|
+
const extensionFileNames = Object.freeze([
|
|
50
|
+
"manifest.json",
|
|
51
|
+
"rules.json",
|
|
52
|
+
"readiness.js",
|
|
53
|
+
] as const);
|
|
54
|
+
const digestPattern = /^[a-f0-9]{64}$/u;
|
|
55
|
+
const unsignedIntegerPattern = /^\d{1,40}$/u;
|
|
56
|
+
const derivationIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Keep Ghostget's authenticated proxy control endpoint outside agent-browser's
|
|
60
|
+
* disposable socket namespace. The UUID-bounded name also stays below Unix
|
|
61
|
+
* domain socket path limits on the supported macOS and Linux hosts.
|
|
62
|
+
*/
|
|
63
|
+
export function derivationGuardControlSocketPath(derivationId: string): string {
|
|
64
|
+
if (!derivationIdPattern.test(derivationId)) {
|
|
65
|
+
throw new Error("derivation proxy control identity is malformed");
|
|
66
|
+
}
|
|
67
|
+
const root = process.platform === "win32" ? tmpdir() : "/tmp";
|
|
68
|
+
// Do not use a `.sock` suffix: agent-browser treats orphan `*.sock` files
|
|
69
|
+
// in its configured socket root as disposable daemon endpoints.
|
|
70
|
+
return join(root, `io-wrench-dp-${derivationId}.ctl`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type GuardDirectoryIdentity = {
|
|
74
|
+
readonly device: string;
|
|
75
|
+
readonly inode: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type GuardPrivateFileEvidence = GuardDirectoryIdentity & {
|
|
79
|
+
readonly byteLength: number;
|
|
80
|
+
readonly sha256: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type DerivationGuardExtension = {
|
|
84
|
+
readonly id: typeof DERIVATION_GUARD_EXTENSION_ID;
|
|
85
|
+
readonly directoryIdentity: GuardDirectoryIdentity;
|
|
86
|
+
readonly files: Readonly<Record<(typeof extensionFileNames)[number], GuardPrivateFileEvidence>>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type DerivationProxyHelperConfig = {
|
|
90
|
+
readonly schemaVersion: 1;
|
|
91
|
+
readonly kind: "wrench-derivation-proxy-config";
|
|
92
|
+
readonly derivationId: string;
|
|
93
|
+
readonly directoryIdentity: GuardDirectoryIdentity;
|
|
94
|
+
readonly socketDirectory: string;
|
|
95
|
+
readonly socketIdentity: GuardDirectoryIdentity;
|
|
96
|
+
readonly browserDomains: readonly string[];
|
|
97
|
+
readonly parentOwner: ProcessOwnerIdentity;
|
|
98
|
+
readonly controlNonce: string;
|
|
99
|
+
readonly policySha256: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type DerivationProxyHelperReady = {
|
|
103
|
+
readonly schemaVersion: 1;
|
|
104
|
+
readonly kind: "wrench-derivation-proxy-ready";
|
|
105
|
+
readonly derivationId: string;
|
|
106
|
+
readonly policySha256: string;
|
|
107
|
+
readonly port: number;
|
|
108
|
+
readonly owner: ProcessOwnerIdentity;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export type DerivationNetworkGuard = {
|
|
112
|
+
readonly schemaVersion: 1;
|
|
113
|
+
readonly kind: "contained-mv3-dnr-proxy";
|
|
114
|
+
readonly extension: DerivationGuardExtension;
|
|
115
|
+
readonly proxy: {
|
|
116
|
+
readonly policySha256: string;
|
|
117
|
+
readonly controlNonce: string;
|
|
118
|
+
readonly port: number;
|
|
119
|
+
readonly owner: ProcessOwnerIdentity;
|
|
120
|
+
readonly parentOwner: ProcessOwnerIdentity;
|
|
121
|
+
readonly configFile: GuardPrivateFileEvidence;
|
|
122
|
+
readonly readyFile: GuardPrivateFileEvidence;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type DnrRule = {
|
|
127
|
+
readonly id: number;
|
|
128
|
+
readonly priority: number;
|
|
129
|
+
readonly action: { readonly type: "allow" | "block" };
|
|
130
|
+
readonly condition: {
|
|
131
|
+
readonly regexFilter: string;
|
|
132
|
+
readonly isUrlFilterCaseSensitive: false;
|
|
133
|
+
readonly resourceTypes: typeof derivationGuardResourceTypes;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
type GuardRuntimeCase = {
|
|
138
|
+
readonly label: string;
|
|
139
|
+
readonly url: string;
|
|
140
|
+
readonly type: (typeof derivationGuardResourceTypes)[number];
|
|
141
|
+
readonly ruleId: number;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
function currentUserOwns(uid: number | bigint): boolean {
|
|
145
|
+
const current = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
146
|
+
return current === undefined || uid === (typeof uid === "bigint" ? BigInt(current) : current);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function escapeRegex(value: string): string {
|
|
150
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function domainAllowRegex(domain: string): string {
|
|
154
|
+
const wildcard = domain.startsWith("*.");
|
|
155
|
+
const base = wildcard ? domain.slice(2) : domain;
|
|
156
|
+
const host = wildcard
|
|
157
|
+
? `(?:[a-z0-9-]+\\.)*${escapeRegex(base)}`
|
|
158
|
+
: escapeRegex(base);
|
|
159
|
+
return `^(?:https|wss)://${host}(?::[0-9]{1,5})?(?:[/?]|$)`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function domainRuleId(index: number): number {
|
|
163
|
+
return 100 + index;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function derivationGuardRules(browserDomains: readonly string[]): readonly DnrRule[] {
|
|
167
|
+
return Object.freeze([
|
|
168
|
+
{
|
|
169
|
+
id: 1,
|
|
170
|
+
priority: 1,
|
|
171
|
+
action: { type: "block" },
|
|
172
|
+
condition: {
|
|
173
|
+
regexFilter: "^(?:http|https|ws|wss)://",
|
|
174
|
+
isUrlFilterCaseSensitive: false,
|
|
175
|
+
resourceTypes: derivationGuardResourceTypes,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
...browserDomains.map((domain, index): DnrRule => ({
|
|
179
|
+
id: domainRuleId(index),
|
|
180
|
+
priority: 2,
|
|
181
|
+
action: { type: "allow" },
|
|
182
|
+
condition: {
|
|
183
|
+
regexFilter: domainAllowRegex(domain),
|
|
184
|
+
isUrlFilterCaseSensitive: false,
|
|
185
|
+
resourceTypes: derivationGuardResourceTypes,
|
|
186
|
+
},
|
|
187
|
+
})),
|
|
188
|
+
]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function runtimeCases(browserDomains: readonly string[]): readonly GuardRuntimeCase[] {
|
|
192
|
+
const cases: GuardRuntimeCase[] = [];
|
|
193
|
+
for (const [index, domain] of browserDomains.entries()) {
|
|
194
|
+
const base = domain.startsWith("*.") ? domain.slice(2) : domain;
|
|
195
|
+
const host = domain.startsWith("*.") ? `wrench-check.${base}` : base;
|
|
196
|
+
const ruleId = domainRuleId(index);
|
|
197
|
+
cases.push(
|
|
198
|
+
{ label: `allow-${index}-https`, url: `https://${host}/wrench-check`, type: "xmlhttprequest", ruleId },
|
|
199
|
+
{ label: `allow-${index}-wss`, url: `wss://${host}/wrench-check`, type: "websocket", ruleId },
|
|
200
|
+
{ label: `allow-${index}-uppercase`, url: `https://${host.toUpperCase()}/wrench-check`, type: "main_frame", ruleId },
|
|
201
|
+
{ label: `allow-${index}-port`, url: `https://${host}:8443/wrench-check`, type: "sub_frame", ruleId },
|
|
202
|
+
{ label: `block-${index}-http`, url: `http://${host}/wrench-check`, type: "xmlhttprequest", ruleId: 1 },
|
|
203
|
+
{ label: `block-${index}-ws`, url: `ws://${host}/wrench-check`, type: "websocket", ruleId: 1 },
|
|
204
|
+
{ label: `block-${index}-trailing-dot`, url: `https://${host}./wrench-check`, type: "main_frame", ruleId: 1 },
|
|
205
|
+
{ label: `block-${index}-suffix`, url: `https://${host}.invalid/wrench-check`, type: "main_frame", ruleId: 1 },
|
|
206
|
+
{ label: `block-${index}-sibling`, url: `https://not-${host}/wrench-check`, type: "main_frame", ruleId: 1 },
|
|
207
|
+
);
|
|
208
|
+
if (domain.startsWith("*.")) {
|
|
209
|
+
cases.push({
|
|
210
|
+
label: `allow-${index}-wildcard-base`,
|
|
211
|
+
url: `https://${base}/wrench-check`,
|
|
212
|
+
type: "main_frame",
|
|
213
|
+
ruleId,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const first = browserDomains[0];
|
|
218
|
+
if (first !== undefined) {
|
|
219
|
+
const base = first.startsWith("*.") ? first.slice(2) : first;
|
|
220
|
+
const host = first.startsWith("*.") ? `wrench-check.${base}` : base;
|
|
221
|
+
for (const type of derivationGuardResourceTypes) {
|
|
222
|
+
cases.push({
|
|
223
|
+
label: `resource-${type}`,
|
|
224
|
+
url: `${type === "websocket" ? "wss" : "https"}://${host}/resource-${type}`,
|
|
225
|
+
type,
|
|
226
|
+
ruleId: domainRuleId(0),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
cases.push({
|
|
231
|
+
label: "block-unallowed-punycode",
|
|
232
|
+
url: "https://xn--wrench-unallowed-9d0b.invalid/wrench-check",
|
|
233
|
+
type: "main_frame",
|
|
234
|
+
ruleId: 1,
|
|
235
|
+
});
|
|
236
|
+
return Object.freeze(cases);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function derivationGuardReadinessCheckCount(
|
|
240
|
+
browserDomains: readonly string[],
|
|
241
|
+
): number {
|
|
242
|
+
return 3 + derivationGuardRules(browserDomains).length + runtimeCases(browserDomains).length;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function readinessPolicySha256(
|
|
246
|
+
rules: readonly DnrRule[],
|
|
247
|
+
cases: readonly GuardRuntimeCase[],
|
|
248
|
+
): string {
|
|
249
|
+
return sha256(canonicalJson({
|
|
250
|
+
schemaVersion: 1,
|
|
251
|
+
extensionId: DERIVATION_GUARD_EXTENSION_ID,
|
|
252
|
+
rules,
|
|
253
|
+
cases,
|
|
254
|
+
}));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function derivationGuardReadinessPolicySha256(
|
|
258
|
+
browserDomains: readonly string[],
|
|
259
|
+
): string {
|
|
260
|
+
return readinessPolicySha256(
|
|
261
|
+
derivationGuardRules(browserDomains),
|
|
262
|
+
runtimeCases(browserDomains),
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function readinessScript(
|
|
267
|
+
rules: readonly DnrRule[],
|
|
268
|
+
cases: readonly GuardRuntimeCase[],
|
|
269
|
+
): string {
|
|
270
|
+
const regexes = rules.map((rule) => ({
|
|
271
|
+
id: rule.id,
|
|
272
|
+
regex: rule.condition.regexFilter,
|
|
273
|
+
}));
|
|
274
|
+
const policySha256 = readinessPolicySha256(rules, cases);
|
|
275
|
+
return `"use strict";\nconst extensionId=${JSON.stringify(DERIVATION_GUARD_EXTENSION_ID)};\nconst policySha256=${JSON.stringify(policySha256)};\nconst regexes=${JSON.stringify(regexes)};\nconst cases=${JSON.stringify(cases)};\nglobalThis.__ghostgetCheckGuard=async()=>{let checks=0;try{if(chrome.runtime.id!==extensionId)throw new Error("id");checks+=1;const enabled=await chrome.declarativeNetRequest.getEnabledRulesets();if(!Array.isArray(enabled)||enabled.length!==1||enabled[0]!=="rules")throw new Error("ruleset");checks+=1;const disabled=await chrome.declarativeNetRequest.getDisabledRuleIds({rulesetId:"rules"});if(!Array.isArray(disabled)||disabled.length!==0)throw new Error("disabled");checks+=1;for(const item of regexes){const support=await chrome.declarativeNetRequest.isRegexSupported({isCaseSensitive:false,regex:item.regex});if(!support||support.isSupported!==true)throw new Error("regex");checks+=1}for(const item of cases){const result=await chrome.declarativeNetRequest.testMatchOutcome({type:item.type,url:item.url});if(!result||!Array.isArray(result.matchedRules)||result.matchedRules.length!==1)throw new Error("outcome");const match=result.matchedRules[0];if(!match||match.ruleId!==item.ruleId||match.rulesetId!=="rules")throw new Error("rule");checks+=1}return{schemaVersion:1,ok:true,extensionId,policySha256,checks}}catch{return{schemaVersion:1,ok:false,extensionId:"",policySha256:"",checks:0}}};\n`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function derivationGuardExtensionFiles(
|
|
279
|
+
browserDomains: readonly string[],
|
|
280
|
+
): Readonly<Record<(typeof extensionFileNames)[number], string>> {
|
|
281
|
+
const rules = derivationGuardRules(browserDomains);
|
|
282
|
+
const manifest = {
|
|
283
|
+
manifest_version: 3,
|
|
284
|
+
name: "Ghostget Derivation Network Guard",
|
|
285
|
+
version: "1.0.0",
|
|
286
|
+
key: extensionPublicKey,
|
|
287
|
+
background: { service_worker: "readiness.js" },
|
|
288
|
+
permissions: ["declarativeNetRequest", "declarativeNetRequestFeedback"],
|
|
289
|
+
declarative_net_request: {
|
|
290
|
+
rule_resources: [{ id: "rules", enabled: true, path: "rules.json" }],
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
return Object.freeze({
|
|
294
|
+
"manifest.json": `${JSON.stringify(manifest)}\n`,
|
|
295
|
+
"rules.json": `${JSON.stringify(rules)}\n`,
|
|
296
|
+
"readiness.js": readinessScript(rules, runtimeCases(browserDomains)),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function extensionIdFromKey(): string {
|
|
301
|
+
const digest = createHash("sha256")
|
|
302
|
+
.update(Buffer.from(extensionPublicKey, "base64"))
|
|
303
|
+
.digest("hex")
|
|
304
|
+
.slice(0, 32);
|
|
305
|
+
return [...digest].map((value) => "abcdefghijklmnop"[Number.parseInt(value, 16)]).join("");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function inspectDirectory(path: string): GuardDirectoryIdentity {
|
|
309
|
+
try {
|
|
310
|
+
const stats = lstatSync(path, { bigint: true });
|
|
311
|
+
if (
|
|
312
|
+
!stats.isDirectory()
|
|
313
|
+
|| stats.isSymbolicLink()
|
|
314
|
+
|| !currentUserOwns(stats.uid)
|
|
315
|
+
|| (stats.mode & 0o777n) !== 0o700n
|
|
316
|
+
) throw new Error("unsafe");
|
|
317
|
+
return { device: stats.dev.toString(), inode: stats.ino.toString() };
|
|
318
|
+
} catch {
|
|
319
|
+
throw new Error("derivation network guard directory is unavailable or unsafe");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function sameIdentity(left: GuardDirectoryIdentity, right: GuardDirectoryIdentity): boolean {
|
|
324
|
+
return left.device === right.device && left.inode === right.inode;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function writeGuardPrivateFile(path: string, content: string): GuardPrivateFileEvidence {
|
|
328
|
+
let descriptor: number | null = null;
|
|
329
|
+
let openedIdentity: GuardDirectoryIdentity | null = null;
|
|
330
|
+
let completed = false;
|
|
331
|
+
try {
|
|
332
|
+
descriptor = openSync(
|
|
333
|
+
path,
|
|
334
|
+
constants.O_WRONLY
|
|
335
|
+
| constants.O_CREAT
|
|
336
|
+
| constants.O_EXCL
|
|
337
|
+
| ("O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0),
|
|
338
|
+
0o600,
|
|
339
|
+
);
|
|
340
|
+
const opened = fstatSync(descriptor, { bigint: true });
|
|
341
|
+
openedIdentity = { device: opened.dev.toString(), inode: opened.ino.toString() };
|
|
342
|
+
fchmodSync(descriptor, 0o600);
|
|
343
|
+
const bytes = Buffer.from(content, "utf8");
|
|
344
|
+
let offset = 0;
|
|
345
|
+
while (offset < bytes.byteLength) {
|
|
346
|
+
offset += writeSync(descriptor, bytes, offset, bytes.byteLength - offset);
|
|
347
|
+
}
|
|
348
|
+
fsyncSync(descriptor);
|
|
349
|
+
const stats = fstatSync(descriptor, { bigint: true });
|
|
350
|
+
if (
|
|
351
|
+
!stats.isFile()
|
|
352
|
+
|| !currentUserOwns(stats.uid)
|
|
353
|
+
|| (stats.mode & 0o777n) !== 0o600n
|
|
354
|
+
|| stats.size !== BigInt(bytes.byteLength)
|
|
355
|
+
) throw new Error("derivation network guard file could not be secured");
|
|
356
|
+
const evidence = {
|
|
357
|
+
device: stats.dev.toString(),
|
|
358
|
+
inode: stats.ino.toString(),
|
|
359
|
+
byteLength: bytes.byteLength,
|
|
360
|
+
sha256: sha256(content),
|
|
361
|
+
};
|
|
362
|
+
completed = true;
|
|
363
|
+
return evidence;
|
|
364
|
+
} catch {
|
|
365
|
+
throw new Error("derivation network guard file could not be secured");
|
|
366
|
+
} finally {
|
|
367
|
+
if (descriptor !== null) {
|
|
368
|
+
if (!completed && openedIdentity === null) {
|
|
369
|
+
try {
|
|
370
|
+
const stats = fstatSync(descriptor, { bigint: true });
|
|
371
|
+
openedIdentity = { device: stats.dev.toString(), inode: stats.ino.toString() };
|
|
372
|
+
} catch {
|
|
373
|
+
// Without an exact identity, preserve the path instead of unlinking.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
closeSync(descriptor);
|
|
378
|
+
} catch {
|
|
379
|
+
// The categorical write failure below must not expose a local path.
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (!completed && openedIdentity !== null) {
|
|
383
|
+
try {
|
|
384
|
+
const current = lstatSync(path, { bigint: true });
|
|
385
|
+
if (
|
|
386
|
+
current.dev.toString() === openedIdentity.device
|
|
387
|
+
&& current.ino.toString() === openedIdentity.inode
|
|
388
|
+
) unlinkSync(path);
|
|
389
|
+
} catch {
|
|
390
|
+
// A changed path is intentionally preserved.
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function readGuardPrivateFile(
|
|
397
|
+
path: string,
|
|
398
|
+
maximumBytes = 4 * 1024 * 1024,
|
|
399
|
+
): { readonly content: string; readonly evidence: GuardPrivateFileEvidence } {
|
|
400
|
+
let descriptor: number | null = null;
|
|
401
|
+
try {
|
|
402
|
+
descriptor = openSync(
|
|
403
|
+
path,
|
|
404
|
+
constants.O_RDONLY | ("O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0),
|
|
405
|
+
);
|
|
406
|
+
const before = fstatSync(descriptor, { bigint: true });
|
|
407
|
+
if (
|
|
408
|
+
!before.isFile()
|
|
409
|
+
|| !currentUserOwns(before.uid)
|
|
410
|
+
|| (before.mode & 0o777n) !== 0o600n
|
|
411
|
+
|| before.size < 1n
|
|
412
|
+
|| before.size > BigInt(maximumBytes)
|
|
413
|
+
) throw new Error("derivation network guard file is unavailable or unsafe");
|
|
414
|
+
const bytes = Buffer.alloc(Number(before.size));
|
|
415
|
+
let offset = 0;
|
|
416
|
+
while (offset < bytes.byteLength) {
|
|
417
|
+
const count = readSync(descriptor, bytes, offset, bytes.byteLength - offset, null);
|
|
418
|
+
if (count === 0) throw new Error("derivation network guard file changed size");
|
|
419
|
+
offset += count;
|
|
420
|
+
}
|
|
421
|
+
const after = fstatSync(descriptor, { bigint: true });
|
|
422
|
+
if (
|
|
423
|
+
before.dev !== after.dev
|
|
424
|
+
|| before.ino !== after.ino
|
|
425
|
+
|| before.size !== after.size
|
|
426
|
+
|| before.mtimeNs !== after.mtimeNs
|
|
427
|
+
|| before.ctimeNs !== after.ctimeNs
|
|
428
|
+
) throw new Error("derivation network guard file changed while reading");
|
|
429
|
+
const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
430
|
+
return {
|
|
431
|
+
content,
|
|
432
|
+
evidence: {
|
|
433
|
+
device: before.dev.toString(),
|
|
434
|
+
inode: before.ino.toString(),
|
|
435
|
+
byteLength: bytes.byteLength,
|
|
436
|
+
sha256: sha256(content),
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
} catch {
|
|
440
|
+
throw new Error("derivation network guard file is unavailable or unsafe");
|
|
441
|
+
} finally {
|
|
442
|
+
if (descriptor !== null) {
|
|
443
|
+
try {
|
|
444
|
+
closeSync(descriptor);
|
|
445
|
+
} catch {
|
|
446
|
+
// Keep diagnostics categorical and path-free.
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export function createDerivationGuardExtension(
|
|
453
|
+
directory: string,
|
|
454
|
+
browserDomains: readonly string[],
|
|
455
|
+
): DerivationGuardExtension {
|
|
456
|
+
if (extensionIdFromKey() !== DERIVATION_GUARD_EXTENSION_ID) {
|
|
457
|
+
throw new Error("derivation network guard extension identity changed");
|
|
458
|
+
}
|
|
459
|
+
const extensionDirectory = join(directory, DERIVATION_GUARD_EXTENSION_DIRECTORY);
|
|
460
|
+
try {
|
|
461
|
+
mkdirSync(extensionDirectory, { mode: 0o700 });
|
|
462
|
+
chmodSync(extensionDirectory, 0o700);
|
|
463
|
+
} catch {
|
|
464
|
+
throw new Error("derivation network guard extension could not be created");
|
|
465
|
+
}
|
|
466
|
+
const directoryIdentity = inspectDirectory(extensionDirectory);
|
|
467
|
+
const contents = derivationGuardExtensionFiles(browserDomains);
|
|
468
|
+
const files = Object.fromEntries(extensionFileNames.map((name) => [
|
|
469
|
+
name,
|
|
470
|
+
writeGuardPrivateFile(join(extensionDirectory, name), contents[name]),
|
|
471
|
+
])) as Record<(typeof extensionFileNames)[number], GuardPrivateFileEvidence>;
|
|
472
|
+
if (!sameIdentity(inspectDirectory(extensionDirectory), directoryIdentity)) {
|
|
473
|
+
throw new Error("derivation network guard directory changed during creation");
|
|
474
|
+
}
|
|
475
|
+
return Object.freeze({
|
|
476
|
+
id: DERIVATION_GUARD_EXTENSION_ID,
|
|
477
|
+
directoryIdentity,
|
|
478
|
+
files: Object.freeze(files),
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export function verifyGuardPrivateFile(
|
|
483
|
+
path: string,
|
|
484
|
+
expected: GuardPrivateFileEvidence,
|
|
485
|
+
expectedContent: string,
|
|
486
|
+
): void {
|
|
487
|
+
let descriptor: number | null = null;
|
|
488
|
+
try {
|
|
489
|
+
descriptor = openSync(
|
|
490
|
+
path,
|
|
491
|
+
constants.O_RDONLY | ("O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0),
|
|
492
|
+
);
|
|
493
|
+
const before = fstatSync(descriptor, { bigint: true });
|
|
494
|
+
if (
|
|
495
|
+
!before.isFile()
|
|
496
|
+
|| !currentUserOwns(before.uid)
|
|
497
|
+
|| (before.mode & 0o777n) !== 0o600n
|
|
498
|
+
|| before.dev.toString() !== expected.device
|
|
499
|
+
|| before.ino.toString() !== expected.inode
|
|
500
|
+
|| before.size !== BigInt(expected.byteLength)
|
|
501
|
+
) throw new Error("derivation network guard file changed identity");
|
|
502
|
+
const bytes = Buffer.alloc(expected.byteLength);
|
|
503
|
+
let offset = 0;
|
|
504
|
+
while (offset < bytes.byteLength) {
|
|
505
|
+
const count = readSync(descriptor, bytes, offset, bytes.byteLength - offset, null);
|
|
506
|
+
if (count === 0) throw new Error("derivation network guard file changed size");
|
|
507
|
+
offset += count;
|
|
508
|
+
}
|
|
509
|
+
const after = fstatSync(descriptor, { bigint: true });
|
|
510
|
+
if (
|
|
511
|
+
before.dev !== after.dev
|
|
512
|
+
|| before.ino !== after.ino
|
|
513
|
+
|| before.size !== after.size
|
|
514
|
+
|| before.mtimeNs !== after.mtimeNs
|
|
515
|
+
|| before.ctimeNs !== after.ctimeNs
|
|
516
|
+
|| bytes.toString("utf8") !== expectedContent
|
|
517
|
+
|| sha256(expectedContent) !== expected.sha256
|
|
518
|
+
) throw new Error("derivation network guard file changed content");
|
|
519
|
+
} catch {
|
|
520
|
+
throw new Error("derivation network guard file changed or is unavailable");
|
|
521
|
+
} finally {
|
|
522
|
+
if (descriptor !== null) {
|
|
523
|
+
try {
|
|
524
|
+
closeSync(descriptor);
|
|
525
|
+
} catch {
|
|
526
|
+
// Keep diagnostics categorical and path-free.
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export function verifyDerivationGuardExtension(
|
|
533
|
+
directory: string,
|
|
534
|
+
browserDomains: readonly string[],
|
|
535
|
+
extension: DerivationGuardExtension,
|
|
536
|
+
): void {
|
|
537
|
+
if (extension.id !== DERIVATION_GUARD_EXTENSION_ID || extensionIdFromKey() !== extension.id) {
|
|
538
|
+
throw new Error("derivation network guard extension identity changed");
|
|
539
|
+
}
|
|
540
|
+
const extensionDirectory = join(directory, DERIVATION_GUARD_EXTENSION_DIRECTORY);
|
|
541
|
+
if (!sameIdentity(inspectDirectory(extensionDirectory), extension.directoryIdentity)) {
|
|
542
|
+
throw new Error("derivation network guard directory changed identity");
|
|
543
|
+
}
|
|
544
|
+
const contents = derivationGuardExtensionFiles(browserDomains);
|
|
545
|
+
for (const name of extensionFileNames) {
|
|
546
|
+
verifyGuardPrivateFile(join(extensionDirectory, name), extension.files[name], contents[name]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function derivationProxyPolicySha256(browserDomains: readonly string[]): string {
|
|
551
|
+
return sha256(canonicalJson({
|
|
552
|
+
schemaVersion: 1,
|
|
553
|
+
kind: "wrench-derivation-connect-policy",
|
|
554
|
+
browserDomains,
|
|
555
|
+
allowPrivateNetwork: false,
|
|
556
|
+
transport: "connect-tls-tunnel",
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export function proxyHelperConfigContent(config: DerivationProxyHelperConfig): string {
|
|
561
|
+
return `${JSON.stringify(config)}\n`;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export function proxyHelperReadyContent(ready: DerivationProxyHelperReady): string {
|
|
565
|
+
return `${JSON.stringify(ready)}\n`;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function exactKeys(record: Record<string, unknown>, expected: readonly string[]): boolean {
|
|
569
|
+
const actual = Object.keys(record).sort();
|
|
570
|
+
const sorted = [...expected].sort();
|
|
571
|
+
return actual.length === sorted.length
|
|
572
|
+
&& actual.every((key, index) => key === sorted[index]);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function parseGuardDirectoryIdentity(value: unknown): GuardDirectoryIdentity {
|
|
576
|
+
if (
|
|
577
|
+
typeof value !== "object"
|
|
578
|
+
|| value === null
|
|
579
|
+
|| Array.isArray(value)
|
|
580
|
+
|| !exactKeys(value as Record<string, unknown>, ["device", "inode"])
|
|
581
|
+
) throw new Error("derivation network guard identity is malformed");
|
|
582
|
+
const record = value as Record<string, unknown>;
|
|
583
|
+
if (
|
|
584
|
+
typeof record.device !== "string"
|
|
585
|
+
|| !unsignedIntegerPattern.test(record.device)
|
|
586
|
+
|| typeof record.inode !== "string"
|
|
587
|
+
|| !unsignedIntegerPattern.test(record.inode)
|
|
588
|
+
) throw new Error("derivation network guard identity is malformed");
|
|
589
|
+
return { device: record.device, inode: record.inode };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export function parseGuardFileEvidence(value: unknown): GuardPrivateFileEvidence {
|
|
593
|
+
if (
|
|
594
|
+
typeof value !== "object"
|
|
595
|
+
|| value === null
|
|
596
|
+
|| Array.isArray(value)
|
|
597
|
+
|| !exactKeys(value as Record<string, unknown>, ["device", "inode", "byteLength", "sha256"])
|
|
598
|
+
) throw new Error("derivation network guard file evidence is malformed");
|
|
599
|
+
const record = value as Record<string, unknown>;
|
|
600
|
+
const identity = parseGuardDirectoryIdentity({ device: record.device, inode: record.inode });
|
|
601
|
+
if (
|
|
602
|
+
!Number.isSafeInteger(record.byteLength)
|
|
603
|
+
|| (record.byteLength as number) < 1
|
|
604
|
+
|| (record.byteLength as number) > 4 * 1024 * 1024
|
|
605
|
+
|| typeof record.sha256 !== "string"
|
|
606
|
+
|| !digestPattern.test(record.sha256)
|
|
607
|
+
) throw new Error("derivation network guard file evidence is malformed");
|
|
608
|
+
return {
|
|
609
|
+
...identity,
|
|
610
|
+
byteLength: record.byteLength as number,
|
|
611
|
+
sha256: record.sha256,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function parseOwner(value: unknown): ProcessOwnerIdentity {
|
|
616
|
+
if (
|
|
617
|
+
typeof value !== "object"
|
|
618
|
+
|| value === null
|
|
619
|
+
|| Array.isArray(value)
|
|
620
|
+
|| !exactKeys(value as Record<string, unknown>, ["pid", "bootId", "processStartId"])
|
|
621
|
+
) throw new Error("derivation proxy owner is malformed");
|
|
622
|
+
const record = value as Record<string, unknown>;
|
|
623
|
+
if (
|
|
624
|
+
!Number.isSafeInteger(record.pid)
|
|
625
|
+
|| (record.pid as number) < 1
|
|
626
|
+
|| typeof record.bootId !== "string"
|
|
627
|
+
|| !digestPattern.test(record.bootId)
|
|
628
|
+
|| typeof record.processStartId !== "string"
|
|
629
|
+
|| !digestPattern.test(record.processStartId)
|
|
630
|
+
) throw new Error("derivation proxy owner is malformed");
|
|
631
|
+
return {
|
|
632
|
+
pid: record.pid as number,
|
|
633
|
+
bootId: record.bootId,
|
|
634
|
+
processStartId: record.processStartId,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export function parseDerivationNetworkGuard(value: unknown): DerivationNetworkGuard {
|
|
639
|
+
if (
|
|
640
|
+
typeof value !== "object"
|
|
641
|
+
|| value === null
|
|
642
|
+
|| Array.isArray(value)
|
|
643
|
+
|| !exactKeys(value as Record<string, unknown>, ["schemaVersion", "kind", "extension", "proxy"])
|
|
644
|
+
) throw new Error("derivation network guard metadata is malformed");
|
|
645
|
+
const record = value as Record<string, unknown>;
|
|
646
|
+
if (record.schemaVersion !== 1 || record.kind !== "contained-mv3-dnr-proxy") {
|
|
647
|
+
throw new Error("derivation network guard metadata is malformed");
|
|
648
|
+
}
|
|
649
|
+
if (
|
|
650
|
+
typeof record.extension !== "object"
|
|
651
|
+
|| record.extension === null
|
|
652
|
+
|| Array.isArray(record.extension)
|
|
653
|
+
|| !exactKeys(record.extension as Record<string, unknown>, ["id", "directoryIdentity", "files"])
|
|
654
|
+
) throw new Error("derivation network guard extension metadata is malformed");
|
|
655
|
+
const extensionRecord = record.extension as Record<string, unknown>;
|
|
656
|
+
if (
|
|
657
|
+
extensionRecord.id !== DERIVATION_GUARD_EXTENSION_ID
|
|
658
|
+
|| typeof extensionRecord.files !== "object"
|
|
659
|
+
|| extensionRecord.files === null
|
|
660
|
+
|| Array.isArray(extensionRecord.files)
|
|
661
|
+
|| !exactKeys(extensionRecord.files as Record<string, unknown>, extensionFileNames)
|
|
662
|
+
) throw new Error("derivation network guard extension metadata is malformed");
|
|
663
|
+
const fileRecord = extensionRecord.files as Record<string, unknown>;
|
|
664
|
+
const files = Object.fromEntries(extensionFileNames.map((name) => [
|
|
665
|
+
name,
|
|
666
|
+
parseGuardFileEvidence(fileRecord[name]),
|
|
667
|
+
])) as Record<(typeof extensionFileNames)[number], GuardPrivateFileEvidence>;
|
|
668
|
+
if (
|
|
669
|
+
typeof record.proxy !== "object"
|
|
670
|
+
|| record.proxy === null
|
|
671
|
+
|| Array.isArray(record.proxy)
|
|
672
|
+
|| !exactKeys(record.proxy as Record<string, unknown>, [
|
|
673
|
+
"policySha256", "controlNonce", "port", "owner", "parentOwner", "configFile", "readyFile",
|
|
674
|
+
])
|
|
675
|
+
) throw new Error("derivation network proxy metadata is malformed");
|
|
676
|
+
const proxy = record.proxy as Record<string, unknown>;
|
|
677
|
+
if (
|
|
678
|
+
typeof proxy.policySha256 !== "string"
|
|
679
|
+
|| !digestPattern.test(proxy.policySha256)
|
|
680
|
+
|| typeof proxy.controlNonce !== "string"
|
|
681
|
+
|| !/^[a-f0-9]{64}$/u.test(proxy.controlNonce)
|
|
682
|
+
|| !Number.isSafeInteger(proxy.port)
|
|
683
|
+
|| (proxy.port as number) < 1
|
|
684
|
+
|| (proxy.port as number) > 65_535
|
|
685
|
+
) throw new Error("derivation network proxy metadata is malformed");
|
|
686
|
+
return Object.freeze({
|
|
687
|
+
schemaVersion: 1,
|
|
688
|
+
kind: "contained-mv3-dnr-proxy",
|
|
689
|
+
extension: Object.freeze({
|
|
690
|
+
id: DERIVATION_GUARD_EXTENSION_ID,
|
|
691
|
+
directoryIdentity: parseGuardDirectoryIdentity(extensionRecord.directoryIdentity),
|
|
692
|
+
files: Object.freeze(files),
|
|
693
|
+
}),
|
|
694
|
+
proxy: Object.freeze({
|
|
695
|
+
policySha256: proxy.policySha256,
|
|
696
|
+
controlNonce: proxy.controlNonce,
|
|
697
|
+
port: proxy.port as number,
|
|
698
|
+
owner: parseOwner(proxy.owner),
|
|
699
|
+
parentOwner: parseOwner(proxy.parentOwner),
|
|
700
|
+
configFile: parseGuardFileEvidence(proxy.configFile),
|
|
701
|
+
readyFile: parseGuardFileEvidence(proxy.readyFile),
|
|
702
|
+
}),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
export function parseProxyHelperConfig(value: unknown): DerivationProxyHelperConfig {
|
|
707
|
+
if (
|
|
708
|
+
typeof value !== "object"
|
|
709
|
+
|| value === null
|
|
710
|
+
|| Array.isArray(value)
|
|
711
|
+
|| !exactKeys(value as Record<string, unknown>, [
|
|
712
|
+
"schemaVersion", "kind", "derivationId", "directoryIdentity", "socketDirectory",
|
|
713
|
+
"socketIdentity", "browserDomains", "parentOwner", "controlNonce", "policySha256",
|
|
714
|
+
])
|
|
715
|
+
) throw new Error("derivation proxy helper config is malformed");
|
|
716
|
+
const record = value as Record<string, unknown>;
|
|
717
|
+
if (
|
|
718
|
+
record.schemaVersion !== 1
|
|
719
|
+
|| record.kind !== "wrench-derivation-proxy-config"
|
|
720
|
+
|| typeof record.derivationId !== "string"
|
|
721
|
+
|| !derivationIdPattern.test(record.derivationId)
|
|
722
|
+
|| typeof record.socketDirectory !== "string"
|
|
723
|
+
|| record.socketDirectory.length < 1
|
|
724
|
+
|| record.socketDirectory.length > 4_096
|
|
725
|
+
|| record.socketDirectory.includes("\0")
|
|
726
|
+
|| !Array.isArray(record.browserDomains)
|
|
727
|
+
|| record.browserDomains.length < 1
|
|
728
|
+
|| record.browserDomains.length > 100
|
|
729
|
+
|| record.browserDomains.some((domain) => typeof domain !== "string")
|
|
730
|
+
|| typeof record.controlNonce !== "string"
|
|
731
|
+
|| !/^[a-f0-9]{64}$/u.test(record.controlNonce)
|
|
732
|
+
|| typeof record.policySha256 !== "string"
|
|
733
|
+
|| !digestPattern.test(record.policySha256)
|
|
734
|
+
) throw new Error("derivation proxy helper config is malformed");
|
|
735
|
+
const browserDomains = record.browserDomains.map((domain) => String(domain));
|
|
736
|
+
if (derivationProxyPolicySha256(browserDomains) !== record.policySha256) {
|
|
737
|
+
throw new Error("derivation proxy helper policy changed");
|
|
738
|
+
}
|
|
739
|
+
return Object.freeze({
|
|
740
|
+
schemaVersion: 1,
|
|
741
|
+
kind: "wrench-derivation-proxy-config",
|
|
742
|
+
derivationId: record.derivationId,
|
|
743
|
+
directoryIdentity: parseGuardDirectoryIdentity(record.directoryIdentity),
|
|
744
|
+
socketDirectory: record.socketDirectory,
|
|
745
|
+
socketIdentity: parseGuardDirectoryIdentity(record.socketIdentity),
|
|
746
|
+
browserDomains: Object.freeze(browserDomains),
|
|
747
|
+
parentOwner: parseOwner(record.parentOwner),
|
|
748
|
+
controlNonce: record.controlNonce,
|
|
749
|
+
policySha256: record.policySha256,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export function parseProxyHelperReady(value: unknown): DerivationProxyHelperReady {
|
|
754
|
+
if (
|
|
755
|
+
typeof value !== "object"
|
|
756
|
+
|| value === null
|
|
757
|
+
|| Array.isArray(value)
|
|
758
|
+
|| !exactKeys(value as Record<string, unknown>, [
|
|
759
|
+
"schemaVersion", "kind", "derivationId", "policySha256", "port", "owner",
|
|
760
|
+
])
|
|
761
|
+
) throw new Error("derivation proxy helper readiness is malformed");
|
|
762
|
+
const record = value as Record<string, unknown>;
|
|
763
|
+
if (
|
|
764
|
+
record.schemaVersion !== 1
|
|
765
|
+
|| record.kind !== "wrench-derivation-proxy-ready"
|
|
766
|
+
|| typeof record.derivationId !== "string"
|
|
767
|
+
|| !derivationIdPattern.test(record.derivationId)
|
|
768
|
+
|| typeof record.policySha256 !== "string"
|
|
769
|
+
|| !digestPattern.test(record.policySha256)
|
|
770
|
+
|| !Number.isSafeInteger(record.port)
|
|
771
|
+
|| (record.port as number) < 1
|
|
772
|
+
|| (record.port as number) > 65_535
|
|
773
|
+
) throw new Error("derivation proxy helper readiness is malformed");
|
|
774
|
+
return Object.freeze({
|
|
775
|
+
schemaVersion: 1,
|
|
776
|
+
kind: "wrench-derivation-proxy-ready",
|
|
777
|
+
derivationId: record.derivationId,
|
|
778
|
+
policySha256: record.policySha256,
|
|
779
|
+
port: record.port as number,
|
|
780
|
+
owner: parseOwner(record.owner),
|
|
781
|
+
});
|
|
782
|
+
}
|