From ea50be0c6b58ff367f20db6c6d7f2ab16164c78e Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Thu, 30 May 2024 10:55:50 -0700 Subject: [PATCH 1/8] syncing semi broken state --- .vscode/settings.json | 4 +- typescript/package-lock.json | 350 +++--------------- typescript/packages/basic-ifc/package.json | 46 +++ typescript/packages/basic-ifc/src/ifc.ts | 314 ++++++++++++++++ typescript/packages/basic-ifc/src/ifc_test.ts | 93 +++++ typescript/packages/basic-ifc/src/index.ts | 29 ++ typescript/packages/basic-ifc/tsconfig.json | 9 + 7 files changed, 547 insertions(+), 298 deletions(-) create mode 100644 typescript/packages/basic-ifc/package.json create mode 100644 typescript/packages/basic-ifc/src/ifc.ts create mode 100644 typescript/packages/basic-ifc/src/ifc_test.ts create mode 100644 typescript/packages/basic-ifc/src/index.ts create mode 100644 typescript/packages/basic-ifc/tsconfig.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 035012db2..cbac5697b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "deno.enable": false -} \ No newline at end of file + "deno.enable": true +} diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 3798405cd..fa378424d 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -32,54 +32,18 @@ "url": "https://github.com/sponsors/philsturgeon" } }, - "node_modules/@bytecodealliance/jco": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.2.4.tgz", - "integrity": "sha512-uuOm9UkYqWp5uElYDNzlhjbdrAmczEvETgQdI1hFTk79h+mrmHPRV32pgRP5o1eHVwMIpuk4XQkDIhFbksurUw==", - "dev": true, - "workspaces": [ - "packages/preview2-shim" - ], - "dependencies": { - "@bytecodealliance/preview2-shim": "^0.16.2", - "binaryen": "^116.0.0", - "chalk-template": "^1", - "commander": "^12", - "mkdirp": "^3", - "ora": "^8", - "terser": "^5" - }, - "bin": { - "jco": "src/jco.js" - } - }, - "node_modules/@bytecodealliance/jco/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@bytecodealliance/preview2-shim": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.16.2.tgz", "integrity": "sha512-36MwesmbLSf3Y5/OHcS85iBaF0N92CQ4gpjtDVKSbrjxmrBKCWlWVfoQ03F/cqDg8k5K7pzVaVBH0XBIbTCfTQ==", "dev": true }, - "node_modules/@commontools/runtime-demo": { - "resolved": "packages/runtime-demo", + "node_modules/@commontools/basic-ifc": { + "resolved": "packages/basic-ifc", "link": true }, - "node_modules/@commontools/std": { - "resolved": "packages/std", + "node_modules/@commontools/runtime-demo": { + "resolved": "packages/runtime-demo", "link": true }, "node_modules/@commontools/usuba-api": { @@ -171,6 +135,8 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -185,6 +151,8 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -194,6 +162,8 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -203,6 +173,8 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -212,13 +184,17 @@ "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -399,18 +375,6 @@ "node": ">=0.4.0" } }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -442,16 +406,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/binaryen": { - "version": "116.0.0", - "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0.tgz", - "integrity": "sha512-Hp0dXC6Cb/rTwWEoUS2BRghObE7g/S9umKtxuTDt3f61G6fNTE/YVew/ezyy3IdHcLx3f17qfh6LwETgCfvWkQ==", - "dev": true, - "bin": { - "wasm-opt": "bin/wasm-opt", - "wasm2js": "bin/wasm2js" - } - }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -468,7 +422,9 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/c12": { "version": "1.10.0", @@ -502,33 +458,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk-template": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", - "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", - "dev": true, - "dependencies": { - "chalk": "^5.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -571,33 +500,6 @@ "consola": "^3.2.3" } }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -671,12 +573,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, "node_modules/esbuild": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", @@ -715,6 +611,10 @@ "@esbuild/win32-x64": "0.20.2" } }, + "node_modules/example-package": { + "resolved": "packages/grammar-experiments", + "link": true + }, "node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -811,18 +711,6 @@ "node": ">=8" } }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -935,18 +823,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -968,18 +844,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-unicode-supported": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", - "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1041,34 +905,6 @@ "@types/trusted-types": "^2.0.2" } }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -1282,29 +1118,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", - "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", - "dev": true, - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1441,46 +1254,6 @@ "node": ">=8.10.0" } }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -1608,55 +1381,13 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -1691,6 +1422,8 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -1708,7 +1441,9 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -1865,6 +1600,16 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "packages/basic-ifc": { + "name": "@commontools/basic-ifc", + "version": "0.0.1", + "license": "UNLICENSED", + "devDependencies": { + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "wireit": "^0.14.4" + } + }, "packages/example-package": { "version": "0.0.1", "extraneous": true, @@ -1876,6 +1621,17 @@ "wireit": "^0.14.4" } }, + "packages/grammar-experiments": { + "name": "example-package", + "version": "0.0.1", + "license": "UNLICENSED", + "devDependencies": { + "@bytecodealliance/preview2-shim": "^0.16.2", + "typescript": "^5.4.5", + "vite": "^5.2.0", + "wireit": "^0.14.4" + } + }, "packages/runtime-demo": { "name": "@commontools/runtime-demo", "version": "0.0.1", @@ -1891,6 +1647,7 @@ "packages/std": { "name": "@commontools/std", "version": "0.0.1", + "extraneous": true, "license": "UNLICENSED", "devDependencies": { "@bytecodealliance/jco": "^1.2.4", @@ -1947,6 +1704,7 @@ "devDependencies": { "@commontools/usuba-api": "^0.0.1", "@commontools/usuba-sw": "^0.0.1", + "tslib": "^2.6.2", "typescript": "^5.2.2", "vite": "^5.2.0", "wireit": "^0.14.4" diff --git a/typescript/packages/basic-ifc/package.json b/typescript/packages/basic-ifc/package.json new file mode 100644 index 000000000..0f891e1c8 --- /dev/null +++ b/typescript/packages/basic-ifc/package.json @@ -0,0 +1,46 @@ +{ + "name": "@commontools/basic-ifc", + "author": "The Common Authors", + "version": "0.0.1", + "description": "A Runtime for managing the invocation of Usuba-produced Modules", + "license": "UNLICENSED", + "private": true, + "type": "module", + "scripts": { + "build": "wireit", + "clean": "wireit" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/commontoolsinc/labs.git" + }, + "bugs": { + "url": "https://github.com/commontoolsinc/labs/issues" + }, + "homepage": "https://github.com/commontoolsinc/labs#readme", + "exports": "./lib/index.js", + "files": [ + "./lib/*.js" + ], + "dependencies": {}, + "devDependencies": { + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "wireit": "^0.14.4" + }, + "wireit": { + "build": { + "dependencies": [], + "files": [ + "./src/**/*" + ], + "output": [ + "./lib/**/*" + ], + "command": "tsc --build -f" + }, + "clean": { + "command": "rm -rf ./lib ./.wireit" + } + } +} diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts new file mode 100644 index 000000000..b934c7d82 --- /dev/null +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -0,0 +1,314 @@ +type IntegrityOrConfidentiality = "integrity" | "confidentiality"; +type LatticeVariable = `\$${string}-${IntegrityOrConfidentiality}`; +type LatticeGroundedPrincipal = Exclude; + +function isLatticeVariable( + principal: LatticePrincipal +): principal is LatticeVariable { + return typeof principal === "string" && principal.startsWith("$"); +} + +function isLatticeGroundedPrincipal( + principal: LatticePrincipal +): principal is LatticeGroundedPrincipal { + return typeof principal === "string" && !principal.startsWith("$"); +} + +type LatticePrincipal = + | LatticeGroundedPrincipal + | LatticeVariable + | ["join" | "meet", LatticePrincipal[]]; + +type Label = { integrity: LatticePrincipal; confidentiality: LatticePrincipal }; + +type Constraint = [Label, Label]; + +const $label = Symbol("label"); +type State = { + [$label]: Label; + [key: string]: State; +}; + +type Node = { + type?: string; // Currently unused + in: string[]; + out: string[]; +}; + +const [TOP, BOTTOM] = ["TOP", "BOTTOM"] as const as LatticeGroundedPrincipal[]; + +// Each level lists its parents +// If a key is missing, it's assumed to have TOP as its parent +type LatticeRelationships = { + [label: LatticeGroundedPrincipal]: LatticeGroundedPrincipal[]; +}; + +type Lattice = { + parents: LatticeRelationships; + children: LatticeRelationships; +}; + +function computeAllParentsForLabel( + label: string, + lattice: LatticeRelationships, + end: LatticePrincipal +): string[] { + if (label === end) { + return []; + } + const parents = lattice[label] || [end]; + return parents.reduce( + (acc, parent) => [ + ...acc, + ...computeAllParentsForLabel(parent, lattice, end), + ], + parents + ); +} + +// Create a lattice where each key lists all children +function makeLattice(latticeRelationships: LatticeRelationships): Lattice { + const invertedLattice: LatticeRelationships = {}; + + const allKeys = new Set(); + + for (const key in latticeRelationships) { + allKeys.add(key); + for (const parent of latticeRelationships[key]) { + allKeys.add(parent); + if (!invertedLattice[parent]) { + invertedLattice[parent] = []; + } + invertedLattice[parent].push(key); + } + } + + const lattice = { + parents: {} as LatticeRelationships, + children: {} as LatticeRelationships, + }; + + // Compute all parents and children for each key + for (const key in latticeRelationships) + lattice.parents[key] = [ + key, + ...computeAllParentsForLabel(key, latticeRelationships, TOP), + ]; + + for (const key in invertedLattice) + lattice.children[key] = [ + key, + ...computeAllParentsForLabel(key, invertedLattice, BOTTOM), + ]; + + for (const key of allKeys) { + if (!lattice.parents[key]) { + lattice.parents[key] = [key, TOP]; + } + if (!lattice.children[key]) { + lattice.children[key] = [key, BOTTOM]; + } + } + + return lattice; +} + +function dedupe(principals: LatticePrincipal[]): LatticePrincipal[] { + const seen = new Set(); + return principals.filter((p) => { + if (seen.has(p)) { + return false; + } + seen.add(p); + return true; + }); +} + +// Computes the join of the principals in the lattice +// Only grounded principals can join, if they are variables, we leave them alone +function join( + principals: LatticePrincipal[], + lattice: Lattice +): LatticePrincipal { + const groundedPrincipals = principals.filter(isLatticeGroundedPrincipal); + const otherPrincipals = dedupe( + principals.filter((p) => !isLatticeGroundedPrincipal(p)) + ); + + const commonParents = groundedPrincipals.reduce( + (acc, principal) => + (lattice.parents[principal] ?? []).filter((parent) => + acc.includes(parent) + ), + lattice.parents[groundedPrincipals[0]] ?? [] + ); + + return otherPrincipals.length + ? ([ + "join", + [ + ...otherPrincipals, + ...(commonParents.length ? [commonParents[0]] : []), + ], + ] as LatticePrincipal) + : commonParents[0] ?? TOP; +} + +function meet( + principals: LatticePrincipal[], + lattice: Lattice +): LatticePrincipal { + const groundedPrincipals = principals.filter(isLatticeGroundedPrincipal); + const otherPrincipals = dedupe( + principals.filter((p) => !isLatticeGroundedPrincipal(p)) + ); + + const commonChildren = groundedPrincipals.reduce( + (acc, principal) => + (lattice.children[principal] ?? []).filter((child) => + acc.includes(child) + ), + lattice.children[groundedPrincipals[0]] ?? [] + ); + + return otherPrincipals.length + ? ([ + "meet", + [ + ...otherPrincipals, + ...(commonChildren.length ? [commonChildren[0]] : []), + ], + ] as LatticePrincipal) + : commonChildren[0] ?? BOTTOM; +} + +function generateConstraints(state: State, bindings: Node[]): Constraint[] { + const constraints: Constraint[] = []; + + // Traverse state and create constraints where labels are set + function traverse(subState: State, path: string[]): void { + if (subState[$label]) { + const label = subState[$label]; + const name = path.join("."); + + constraints.push([ + { + integrity: `\$${name}-integrity`, + confidentiality: `\$${name}-confidentiality`, + }, + label, + ]); + } + for (const key in subState) { + traverse(subState[key], [...path, key]); + } + } + + traverse(state, []); + + for (const binding of bindings) { + const integrity = [ + "meet", + binding.in.map((input) => `\$${input}-integrity`), + ] as LatticePrincipal; + const confidentiality = [ + "join", + binding.in.map((input) => `\$${input}-confidentiality`), + ] as LatticePrincipal; + + binding.out.forEach((output) => { + constraints.push([ + { + integrity: `\$${output}-integrity`, + confidentiality: `\$${output}-confidentiality`, + }, + { integrity, confidentiality }, + ]); + }); + } + + return constraints; +} + +type Substitutions = { [key: LatticeVariable]: LatticeGroundedPrincipal }; +function unify( + constraints: Constraint[], + substitutions: Substitutions, + lattice: Lattice +): Substitutions { + const newSubstitutions = { ...substitutions }; + + // Go through all constraints, and where the right side is a grounded principal, unify it with the left side + + return newSubstitutions; +} + +/* +function solveConstraints( + constraints: [Label[], Label[]][], + lattice: Lattice +): { [key: string]: Label } { + const substitutions: { [key: string]: Label } = {}; + + function unify(label1: Label, label2: Label): Label { + return { + integrity: join(label1.integrity, label2.integrity, lattice), + confidentiality: meet( + label1.confidentiality, + label2.confidentiality, + lattice + ), + }; + } + + for (const [inputs, outputs] of constraints) { + const inferredLabel = inferLabels2(inputs, lattice); + + for (const output of outputs) { + const key = `${output.integrity}-${output.confidentiality}`; + if (substitutions[key]) { + substitutions[key] = unify(substitutions[key], inferredLabel); + } else { + substitutions[key] = inferredLabel; + } + } + } + + return substitutions; +} + +function applySubstitutions( + state: State, + substitutions: { [key: string]: Label } +): State { + const inferredState: State = JSON.parse(JSON.stringify(state)); // Deep copy the state + + function applyToNested(obj: any): void { + for (const key in obj) { + if (obj[key] && typeof obj[key] === "object") { + if ("label" in obj[key]) { + const label = obj[key].label; + const keyLabel = `${label.integrity}-${label.confidentiality}`; + if (substitutions[keyLabel]) { + obj[key].label = substitutions[keyLabel]; + } + } + applyToNested(obj[key]); + } + } + } + + applyToNested(inferredState); + return inferredState; +} +*/ +function inferLabels(state: State, bindings: Node[], lattice: Lattice): State { + /* const constraints = generateConstraints(state, bindings); + const substitutions = solveConstraints(constraints, lattice); + return applySubstitutions(state, substitutions);*/ + return {} as State; +} + +export { type Node, makeLattice, inferLabels, BOTTOM, TOP }; + +export { join, meet }; // internals for testing diff --git a/typescript/packages/basic-ifc/src/ifc_test.ts b/typescript/packages/basic-ifc/src/ifc_test.ts new file mode 100644 index 000000000..2d41a06f9 --- /dev/null +++ b/typescript/packages/basic-ifc/src/ifc_test.ts @@ -0,0 +1,93 @@ +import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { + inferLabels, + type Node, + makeLattice, + join, + meet, + TOP, + BOTTOM, +} from "./ifc.ts"; + +Deno.test("meet and join", () => { + const lattice = makeLattice({ + public: ["trusted cloud"], + "trusted cloud": ["cc", "openai", "anthropic"], + cc: ["ondevice"], + }); + + assertEquals(meet(["public", "trusted cloud"], lattice), "public"); + assertEquals(meet(["trusted cloud", "cc"], lattice), "trusted cloud"); + assertEquals(meet(["cc", "ondevice"], lattice), "cc"); + assertEquals(meet(["public", "ondevice"], lattice), "public"); + + assertEquals(join(["public", "trusted cloud"], lattice), "trusted cloud"); + assertEquals(join(["trusted cloud", "cc"], lattice), "cc"); + + assertEquals(meet(["openai", "anthropic"], lattice), "trusted cloud"); + assertEquals(join(["openai", "anthropic"], lattice), TOP); + + // Google is not in the lattice + assertEquals(meet(["public", "google"], lattice), BOTTOM); + assertEquals(join(["public", "google"], lattice), TOP); +}); + +Deno.test("meet and join with type variables", () => { + const lattice = makeLattice({ + public: ["trusted cloud"], + "trusted cloud": ["cc", "openai", "anthropic"], + cc: ["ondevice"], + }); + + assertEquals(meet(["$foo", "trusted cloud"], lattice), [ + "meet", + ["$foo", "trusted cloud"], + ]); + + assertEquals(meet(["$foo", "$bar"], lattice), ["meet", ["$foo", "$bar"]]); + + assertEquals(meet(["$foo", "public", "trusted cloud"], lattice), [ + "meet", + ["$foo", "public"], + ]); + + assertEquals(join(["$foo", "$foo"], lattice), ["join", ["$foo"]]); +}); + +Deno.test("infer labels", () => { + // Example state and bindings + const initialState = { + bar: { + baz: { + label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, + }, + zab: { + label: { integrity: "public", confidentiality: "public" }, + }, + }, + }; + + const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; + + const lattice = makeLattice({ + public: ["trusted cloud"], + "trusted cloud": ["cc", "openai", "anthropic"], + cc: ["ondevice"], + }); + /* + const inferredState = inferLabels(initialState, bindings, lattice); + + assertEquals(inferredState, { + bar: { + baz: { + label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, + }, + zab: { + label: { integrity: "public", confidentiality: "public" }, + }, + }, + foo: { + label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, + }, + });*/ +}); diff --git a/typescript/packages/basic-ifc/src/index.ts b/typescript/packages/basic-ifc/src/index.ts new file mode 100644 index 000000000..371461852 --- /dev/null +++ b/typescript/packages/basic-ifc/src/index.ts @@ -0,0 +1,29 @@ +import { makeLattice, inferLabels, type Node, BOTTOM, TOP } from "./ifc.ts"; + +// Example state and bindings +const initialState = { + bar: { + baz: { + label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, + }, + zab: { + label: { integrity: "public", confidentiality: "public" }, + }, + }, +}; + +const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; + +const lattice = makeLattice({ + [BOTTOM]: ["public"], + public: ["trusted cloud"], + "trusted cloud": ["cc", "openai", "anthropic"], + cc: ["ondevice"], + [TOP]: [], +}); + +// Infer the state +const inferredState = inferLabels(initialState, bindings, lattice); + +// Accessing the labels +console.log(inferredState); // Output will reflect the inferred labels based on the lattice diff --git a/typescript/packages/basic-ifc/tsconfig.json b/typescript/packages/basic-ifc/tsconfig.json new file mode 100644 index 000000000..f0af7fb66 --- /dev/null +++ b/typescript/packages/basic-ifc/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["es2022", "WebWorker"], + "outDir": "./lib", + "rootDir": "./src" + }, + "include": ["src/**/*"] +} From 051933b8a5235c0a23f427bdc365cedcf067785f Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Thu, 30 May 2024 10:56:16 -0700 Subject: [PATCH 2/8] unsaved changes --- typescript/packages/basic-ifc/src/ifc.ts | 18 +++++++++++++++++- typescript/packages/basic-ifc/src/index.ts | 6 +++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index b934c7d82..e5c678ba8 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -238,7 +238,23 @@ function unify( ): Substitutions { const newSubstitutions = { ...substitutions }; - // Go through all constraints, and where the right side is a grounded principal, unify it with the left side + // Go through all constraints, and where the right side is a grounded + // principal, unify it with the left side. + for (const [left, right] of constraints) { + for (const key in right) { + const groundedPrincipal = right[key]; + if (isLatticeGroundedPrincipal(groundedPrincipal)) { + if (newSubstitutions[groundedPrincipal]) { + newSubstitutions[groundedPrincipal] = join( + [newSubstitutions[groundedPrincipal], left[key]], + lattice + ); + } else { + newSubstitutions[groundedPrincipal] = left[key]; + } + } + } + } return newSubstitutions; } diff --git a/typescript/packages/basic-ifc/src/index.ts b/typescript/packages/basic-ifc/src/index.ts index 371461852..800d03cc1 100644 --- a/typescript/packages/basic-ifc/src/index.ts +++ b/typescript/packages/basic-ifc/src/index.ts @@ -1,4 +1,4 @@ -import { makeLattice, inferLabels, type Node, BOTTOM, TOP } from "./ifc.ts"; +import { makeLattice, inferState, type Binding, BOTTOM, TOP } from "./ifc.s"; // Example state and bindings const initialState = { @@ -12,7 +12,7 @@ const initialState = { }, }; -const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; +const bindings: Binding[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; const lattice = makeLattice({ [BOTTOM]: ["public"], @@ -23,7 +23,7 @@ const lattice = makeLattice({ }); // Infer the state -const inferredState = inferLabels(initialState, bindings, lattice); +const inferredState = inferState(initialState, bindings, lattice); // Accessing the labels console.log(inferredState); // Output will reflect the inferred labels based on the lattice From b739a462da51962902370e040dfc539fb176ffa4 Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Thu, 30 May 2024 16:13:30 -0700 Subject: [PATCH 3/8] renamed a lot, tried a lot. committing --- typescript/packages/basic-ifc/src/ifc.ts | 287 +++++++++++++----- typescript/packages/basic-ifc/src/ifc_test.ts | 54 ++++ 2 files changed, 267 insertions(+), 74 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index e5c678ba8..af443128f 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -1,31 +1,41 @@ type IntegrityOrConfidentiality = "integrity" | "confidentiality"; -type LatticeVariable = `\$${string}-${IntegrityOrConfidentiality}`; -type LatticeGroundedPrincipal = Exclude; +type PrincipalVariable = `\$${string}-${IntegrityOrConfidentiality}`; +type Principal = Exclude; + +type PrincipalExpression = + | Principal + | PrincipalVariable + | ["join" | "meet", PrincipalExpression[]] + | undefined; function isLatticeVariable( - principal: LatticePrincipal -): principal is LatticeVariable { + principal: PrincipalExpression +): principal is PrincipalVariable { return typeof principal === "string" && principal.startsWith("$"); } function isLatticeGroundedPrincipal( - principal: LatticePrincipal -): principal is LatticeGroundedPrincipal { + principal: PrincipalExpression +): principal is Principal { return typeof principal === "string" && !principal.startsWith("$"); } -type LatticePrincipal = - | LatticeGroundedPrincipal - | LatticeVariable - | ["join" | "meet", LatticePrincipal[]]; +function isCombinedPrincipal( + principal: PrincipalExpression +): principal is ["join" | "meet", PrincipalExpression[]] { + return Array.isArray(principal) && principal.length === 2; +} -type Label = { integrity: LatticePrincipal; confidentiality: LatticePrincipal }; +type Label = { + integrity: PrincipalExpression; + confidentiality: PrincipalExpression; +}; -type Constraint = [Label, Label]; +type Constraint = [PrincipalVariable, PrincipalExpression]; -const $label = Symbol("label"); +export const $label = Symbol("label"); type State = { - [$label]: Label; + [$label]?: Label; [key: string]: State; }; @@ -35,12 +45,12 @@ type Node = { out: string[]; }; -const [TOP, BOTTOM] = ["TOP", "BOTTOM"] as const as LatticeGroundedPrincipal[]; +const [TOP, BOTTOM] = ["TOP", "BOTTOM"] as const as Principal[]; // Each level lists its parents // If a key is missing, it's assumed to have TOP as its parent type LatticeRelationships = { - [label: LatticeGroundedPrincipal]: LatticeGroundedPrincipal[]; + [label: Principal]: Principal[]; }; type Lattice = { @@ -51,7 +61,7 @@ type Lattice = { function computeAllParentsForLabel( label: string, lattice: LatticeRelationships, - end: LatticePrincipal + end: PrincipalExpression ): string[] { if (label === end) { return []; @@ -113,12 +123,10 @@ function makeLattice(latticeRelationships: LatticeRelationships): Lattice { return lattice; } -function dedupe(principals: LatticePrincipal[]): LatticePrincipal[] { - const seen = new Set(); +function dedupe(principals: PrincipalExpression[]): PrincipalExpression[] { + const seen = new Set(); return principals.filter((p) => { - if (seen.has(p)) { - return false; - } + if (p === undefined || seen.has(p)) return false; seen.add(p); return true; }); @@ -127,9 +135,9 @@ function dedupe(principals: LatticePrincipal[]): LatticePrincipal[] { // Computes the join of the principals in the lattice // Only grounded principals can join, if they are variables, we leave them alone function join( - principals: LatticePrincipal[], + principals: PrincipalExpression[], lattice: Lattice -): LatticePrincipal { +): PrincipalExpression { const groundedPrincipals = principals.filter(isLatticeGroundedPrincipal); const otherPrincipals = dedupe( principals.filter((p) => !isLatticeGroundedPrincipal(p)) @@ -150,14 +158,14 @@ function join( ...otherPrincipals, ...(commonParents.length ? [commonParents[0]] : []), ], - ] as LatticePrincipal) + ] as PrincipalExpression) : commonParents[0] ?? TOP; } function meet( - principals: LatticePrincipal[], + principals: PrincipalExpression[], lattice: Lattice -): LatticePrincipal { +): PrincipalExpression { const groundedPrincipals = principals.filter(isLatticeGroundedPrincipal); const otherPrincipals = dedupe( principals.filter((p) => !isLatticeGroundedPrincipal(p)) @@ -178,10 +186,50 @@ function meet( ...otherPrincipals, ...(commonChildren.length ? [commonChildren[0]] : []), ], - ] as LatticePrincipal) + ] as PrincipalExpression) : commonChildren[0] ?? BOTTOM; } +// Creates an expression from a list of labels combined by join/meet, inlining +// any nested join/meet expressions +function combine( + op: "join" | "meet", + expressions: PrincipalExpression[] +): PrincipalExpression { + const expressionsToJoin = dedupe( + expressions.flatMap((expression) => { + if (Array.isArray(expression) && expression[0] === op) { + return expression[1]; + } + return expression; + }) + ); + + if (expressionsToJoin.length === 0) return undefined; + + if (expressionsToJoin.length === 1) return expressionsToJoin[0]; + + return [op, expressionsToJoin]; +} + +function generatePrincipalVariable( + name: string, + type: IntegrityOrConfidentiality +): PrincipalVariable { + return `$${name}-${type}` as PrincipalVariable; +} + +// Generate initial constraints from the state and bindings +// +// Output integrity is at most the meet of the input integrities Output +// confidentiality is at least the join of the input confidences +// +// "at most" means `x ∧ y = x`, so we can combine all "at most" constraints with +// a meet. Analogous for "at least" and join. +// +// When a label is in the initial state, and it's forcing a specific label. +// Eventually we could support `base-integrity ∧ $other-integrity` to mean to +// compute all other required integrity? Analogous for confidentiality. function generateConstraints(state: State, bindings: Node[]): Constraint[] { const constraints: Constraint[] = []; @@ -191,13 +239,17 @@ function generateConstraints(state: State, bindings: Node[]): Constraint[] { const label = subState[$label]; const name = path.join("."); - constraints.push([ - { - integrity: `\$${name}-integrity`, - confidentiality: `\$${name}-confidentiality`, - }, - label, - ]); + if (label.integrity) + constraints.push([ + generatePrincipalVariable(name, "integrity"), + label.integrity, + ]); + + if (label.confidentiality) + constraints.push([ + generatePrincipalVariable(name, "confidentiality"), + label.confidentiality, + ]); } for (const key in subState) { traverse(subState[key], [...path, key]); @@ -207,56 +259,143 @@ function generateConstraints(state: State, bindings: Node[]): Constraint[] { traverse(state, []); for (const binding of bindings) { - const integrity = [ + // Compute output constraints based on inputs + const combinedInputintegrity = combine( "meet", - binding.in.map((input) => `\$${input}-integrity`), - ] as LatticePrincipal; - const confidentiality = [ + binding.in.map((input) => generatePrincipalVariable(input, "integrity")) + ); + const combinedInputconfidentiality = combine( "join", - binding.in.map((input) => `\$${input}-confidentiality`), - ] as LatticePrincipal; + binding.in.map((input) => + generatePrincipalVariable(input, "confidentiality") + ) + ); binding.out.forEach((output) => { - constraints.push([ - { - integrity: `\$${output}-integrity`, - confidentiality: `\$${output}-confidentiality`, - }, - { integrity, confidentiality }, - ]); + if (combinedInputintegrity !== undefined) { + const name = generatePrincipalVariable(output, "integrity"); + constraints.push([ + name, + combine("meet", [name, combinedInputintegrity]), + ]); + } + + if (combinedInputconfidentiality !== undefined) { + const name = generatePrincipalVariable(output, "confidentiality"); + constraints.push([ + name, + combine("join", [name, combinedInputconfidentiality]), + ]); + } + }); + + // Compute input constraints based on outputs + const combinedOutputintegrity = combine( + "join", + binding.out.map((output) => + generatePrincipalVariable(output, "integrity") + ) + ); + const combinedOutputconfidentiality = combine( + "meet", + binding.out.map((output) => + generatePrincipalVariable(output, "confidentiality") + ) + ); + + binding.in.forEach((input) => { + if (combinedOutputintegrity !== undefined) { + const name = generatePrincipalVariable(input, "integrity"); + constraints.push([ + name, + combine("join", [name, combinedOutputintegrity]), + ]); + } + + if (combinedOutputconfidentiality !== undefined) { + const name = generatePrincipalVariable(input, "confidentiality"); + constraints.push([ + name, + combine("meet", [name, combinedOutputconfidentiality]), + ]); + } }); } return constraints; } -type Substitutions = { [key: LatticeVariable]: LatticeGroundedPrincipal }; -function unify( - constraints: Constraint[], - substitutions: Substitutions, - lattice: Lattice -): Substitutions { - const newSubstitutions = { ...substitutions }; - - // Go through all constraints, and where the right side is a grounded - // principal, unify it with the left side. - for (const [left, right] of constraints) { - for (const key in right) { - const groundedPrincipal = right[key]; - if (isLatticeGroundedPrincipal(groundedPrincipal)) { - if (newSubstitutions[groundedPrincipal]) { - newSubstitutions[groundedPrincipal] = join( - [newSubstitutions[groundedPrincipal], left[key]], - lattice - ); - } else { - newSubstitutions[groundedPrincipal] = left[key]; - } +type Substitutions = { [key: PrincipalVariable]: PrincipalExpression }; +// Substitution rules, in order of priority: +// - If the variable isn't mentioned on the right side, always substitute it +// - If a subset of an expression is just grounded principals, compute their +// join/meet. +// - If there is only one constraint for a variable, we can substitute it if +// the variable is only mentioned in the first layer. +// - If there are multiple constraints for a variable, and all contain the +// variable, see whether there are more top level joins or meets, and +// substitute the winner together. That is `a = a ∧ b` and `a = a ∧ c` can be +// simplified to `a = a ∧ b ∧ c`. +// +// Function will return the first class of these substitutions it finds. It'll +// return an empty object if no substitutions are found. +function findSubstitutions(constraints: Constraint[]): Substitutions { + const substitutions: Substitutions = {}; + + // Return the deepest level the variable is found in, or 0 if not found. + function maxLevelVariableIsContained( + variable: PrincipalVariable, + expression: PrincipalExpression + ): number { + if (isCombinedPrincipal(expression)) + return ( + 1 + + Math.max( + ...expression[1].map((e) => maxLevelVariableIsContained(variable, e)) + ) + ); + else return expression === variable ? 1 : 0; + } + + // If the variable isn't mentioned on the right side, always substitute it + constraints.forEach(([variable, expression]) => { + const level = maxLevelVariableIsContained(variable, expression); + if (level === 0) substitutions[variable] = expression; + }); + if (Object.keys(substitutions).length > 0) return substitutions; + + // Split unique from non-unique constraints + const uniqueConstraints: Constraint[] = []; + const multipleConstraints: Map = + new Map(); + constraints.forEach(([variable, expression]) => { + if (constraints.filter(([v]) => v === variable).length === 1) { + uniqueConstraints.push([variable, expression]); + } else { + if (!multipleConstraints.has(variable)) { + multipleConstraints.set(variable, [expression]); + } else { + multipleConstraints.get(variable)!.push(expression); } } - } + }); - return newSubstitutions; + // If there is only one constraint for a variable, we can substitute it if + // the variable is only mentioned in the first layer. + uniqueConstraints.forEach(([variable, expression]) => { + const level = maxLevelVariableIsContained(variable, expression); + // level 2 means it's $var = [ "meet"| "join", [$var, ...] ] + if (level == 2) { + const [op, expressions] = expression as [ + "join" | "meet", + PrincipalExpression[] + ]; + substitutions[variable] = [op, expressions.filter((e) => e !== variable)]; + } + }); + if (Object.keys(substitutions).length > 0) return substitutions; + + return substitutions; } /* @@ -325,6 +464,6 @@ function inferLabels(state: State, bindings: Node[], lattice: Lattice): State { return {} as State; } -export { type Node, makeLattice, inferLabels, BOTTOM, TOP }; +export { type Node, type State, makeLattice, inferLabels, BOTTOM, TOP }; -export { join, meet }; // internals for testing +export { join, meet, generateConstraints }; // internals for testing diff --git a/typescript/packages/basic-ifc/src/ifc_test.ts b/typescript/packages/basic-ifc/src/ifc_test.ts index 2d41a06f9..2be48f724 100644 --- a/typescript/packages/basic-ifc/src/ifc_test.ts +++ b/typescript/packages/basic-ifc/src/ifc_test.ts @@ -1,6 +1,9 @@ import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts"; import { inferLabels, + generateConstraints, + type State, + $label, type Node, makeLattice, join, @@ -54,6 +57,57 @@ Deno.test("meet and join with type variables", () => { assertEquals(join(["$foo", "$foo"], lattice), ["join", ["$foo"]]); }); +Deno.test("generate constraints", () => { + const initialState: State = { + bar: { + baz: { + [$label]: { + integrity: "trusted cloud", + confidentiality: "trusted cloud", + }, + }, + zab: { + [$label]: { integrity: "public", confidentiality: "public" }, + }, + }, + }; + const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; + + const constraints = generateConstraints(initialState, bindings); + + assertEquals(constraints, [ + ["$bar.baz-integrity", "trusted cloud"], + ["$bar.baz-confidentiality", "trusted cloud"], + ["$bar.zab-integrity", "public"], + ["$bar.zab-confidentiality", "public"], + [ + "$foo-integrity", + ["meet", ["$foo-integrity", "$bar.baz-integrity", "$bar.zab-integrity"]], + ], + [ + "$foo-confidentiality", + [ + "join", + [ + "$foo-confidentiality", + "$bar.baz-confidentiality", + "$bar.zab-confidentiality", + ], + ], + ], + ["$bar.baz-integrity", ["join", ["$bar.baz-integrity", "$foo-integrity"]]], + [ + "$bar.baz-confidentiality", + ["meet", ["$bar.baz-confidentiality", "$foo-confidentiality"]], + ], + ["$bar.zab-integrity", ["join", ["$bar.zab-integrity", "$foo-integrity"]]], + [ + "$bar.zab-confidentiality", + ["meet", ["$bar.zab-confidentiality", "$foo-confidentiality"]], + ], + ]); +}); + Deno.test("infer labels", () => { // Example state and bindings const initialState = { From 5fe603f4ec686a9917f2e9a4fc7a6e7147b141c6 Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Thu, 30 May 2024 16:31:11 -0700 Subject: [PATCH 4/8] almost there to first inference --- typescript/packages/basic-ifc/src/ifc.ts | 95 ++++++++++-------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index af443128f..2d4fb7585 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -398,69 +398,54 @@ function findSubstitutions(constraints: Constraint[]): Substitutions { return substitutions; } -/* -function solveConstraints( - constraints: [Label[], Label[]][], - lattice: Lattice -): { [key: string]: Label } { - const substitutions: { [key: string]: Label } = {}; - - function unify(label1: Label, label2: Label): Label { - return { - integrity: join(label1.integrity, label2.integrity, lattice), - confidentiality: meet( - label1.confidentiality, - label2.confidentiality, - lattice - ), - }; - } - - for (const [inputs, outputs] of constraints) { - const inferredLabel = inferLabels2(inputs, lattice); - - for (const output of outputs) { - const key = `${output.integrity}-${output.confidentiality}`; - if (substitutions[key]) { - substitutions[key] = unify(substitutions[key], inferredLabel); - } else { - substitutions[key] = inferredLabel; - } - } +function applySubstitutions( + constraints: PrincipalExpression, + substitutions: Substitutions +): PrincipalExpression { + if (isLatticeVariable(constraints)) { + return substitutions[constraints] ?? constraints; + } else if (isLatticeGroundedPrincipal(constraints)) { + return constraints; + } else if (isCombinedPrincipal(constraints)) { + const [op, expressions] = constraints; + return [op, expressions.map((e) => applySubstitutions(e, substitutions))]; } - - return substitutions; } -function applySubstitutions( - state: State, - substitutions: { [key: string]: Label } -): State { - const inferredState: State = JSON.parse(JSON.stringify(state)); // Deep copy the state - - function applyToNested(obj: any): void { - for (const key in obj) { - if (obj[key] && typeof obj[key] === "object") { - if ("label" in obj[key]) { - const label = obj[key].label; - const keyLabel = `${label.integrity}-${label.confidentiality}`; - if (substitutions[keyLabel]) { - obj[key].label = substitutions[keyLabel]; - } - } - applyToNested(obj[key]); - } +function unify(constraints: Constraint[], lattice: Lattice): Constraints { + function traverse(expression: PrincipalExpression): PrincipalExpression { + if (isLatticeVariable(expression)) { + return expression; + } else if (isLatticeGroundedPrincipal(expression)) { + return expression; + } else if (isCombinedPrincipal(expression)) { + const [op, expressions] = expression; + const newExpression = expressions.map((e) => + isCombinedPrincipal(e) ? traverse(e) : e + ); + return (op === "join" ? join : meet)(newExpression, lattice); } } - applyToNested(inferredState); - return inferredState; + let substitutions: { [key: string]: PrincipalExpression } = {}; + do { + constraints = constraints.map(([v, e]) => [v, traverse(e)]); + substitutions = findSubstitutions(constraints); + constraints = constraints.map(([v, e]) => [ + v, + applySubstitutions(e, substitutions), + ]); + } while (Object.keys(substitutions).length > 0); } -*/ + function inferLabels(state: State, bindings: Node[], lattice: Lattice): State { - /* const constraints = generateConstraints(state, bindings); - const substitutions = solveConstraints(constraints, lattice); - return applySubstitutions(state, substitutions);*/ + const constraints = unify(generateConstraints(state, bindings), lattice); + + // Verify that there are no contradictions + // TODO + + // Apply the constraints to the state and show all inferred labels + // For now, this means turning variable names into paths and writing out the state return {} as State; } From 386ec299bd79087e219c178e232c6365ee5b9251 Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Thu, 30 May 2024 16:37:18 -0700 Subject: [PATCH 5/8] got to infinite loop :) --- typescript/packages/basic-ifc/src/ifc.ts | 30 ++++++++++++++++--- typescript/packages/basic-ifc/src/ifc_test.ts | 26 +++++----------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index 2d4fb7585..d6b37cbf3 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -412,7 +412,7 @@ function applySubstitutions( } } -function unify(constraints: Constraint[], lattice: Lattice): Constraints { +function unify(constraints: Constraint[], lattice: Lattice): Constraint[] { function traverse(expression: PrincipalExpression): PrincipalExpression { if (isLatticeVariable(expression)) { return expression; @@ -436,17 +436,39 @@ function unify(constraints: Constraint[], lattice: Lattice): Constraints { applySubstitutions(e, substitutions), ]); } while (Object.keys(substitutions).length > 0); + + return constraints; } -function inferLabels(state: State, bindings: Node[], lattice: Lattice): State { - const constraints = unify(generateConstraints(state, bindings), lattice); +function inferLabels( + initialState: State, + bindings: Node[], + lattice: Lattice +): State { + const constraints = unify( + generateConstraints(initialState, bindings), + lattice + ); // Verify that there are no contradictions // TODO // Apply the constraints to the state and show all inferred labels // For now, this means turning variable names into paths and writing out the state - return {} as State; + const state: State = {}; + for (const [variable, expression] of constraints) { + const [name, type] = variable.slice(1).split("-"); + let current = state; + for (const part of name.split(".")) { + if (!current[part]) current[part] = {}; + current = current[part]; + } + if (!current[$label]) + current[$label] = { integrity: undefined, confidentiality: undefined }; + current[$label][type as IntegrityOrConfidentiality] = expression; + } + + return state; } export { type Node, type State, makeLattice, inferLabels, BOTTOM, TOP }; diff --git a/typescript/packages/basic-ifc/src/ifc_test.ts b/typescript/packages/basic-ifc/src/ifc_test.ts index 2be48f724..022cfa421 100644 --- a/typescript/packages/basic-ifc/src/ifc_test.ts +++ b/typescript/packages/basic-ifc/src/ifc_test.ts @@ -110,17 +110,19 @@ Deno.test("generate constraints", () => { Deno.test("infer labels", () => { // Example state and bindings - const initialState = { + const initialState: State = { bar: { baz: { - label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, + [$label]: { + integrity: "trusted cloud", + confidentiality: "trusted cloud", + }, }, zab: { - label: { integrity: "public", confidentiality: "public" }, + [$label]: { integrity: "public", confidentiality: "public" }, }, }, }; - const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; const lattice = makeLattice({ @@ -128,20 +130,8 @@ Deno.test("infer labels", () => { "trusted cloud": ["cc", "openai", "anthropic"], cc: ["ondevice"], }); - /* + const inferredState = inferLabels(initialState, bindings, lattice); - assertEquals(inferredState, { - bar: { - baz: { - label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, - }, - zab: { - label: { integrity: "public", confidentiality: "public" }, - }, - }, - foo: { - label: { integrity: "trusted cloud", confidentiality: "trusted cloud" }, - }, - });*/ + assertEquals(inferredState, {}); }); From b1a831b8375f9775c8d0a0c2f2bbdf66a75c0556 Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Fri, 31 May 2024 10:37:05 -0700 Subject: [PATCH 6/8] no more infinite loop, first correct test --- typescript/packages/basic-ifc/src/ifc.ts | 29 ++++++--- typescript/packages/basic-ifc/src/ifc_test.ts | 65 ++++++++++++++++++- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index d6b37cbf3..87fc9cd74 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -339,8 +339,12 @@ type Substitutions = { [key: PrincipalVariable]: PrincipalExpression }; // // Function will return the first class of these substitutions it finds. It'll // return an empty object if no substitutions are found. -function findSubstitutions(constraints: Constraint[]): Substitutions { - const substitutions: Substitutions = {}; +function findSubstitutions( + allConstraints: Constraint[], + substitutions: Substitutions +): Substitutions { + const newSubstitutions: Substitutions = {}; + const constraints = allConstraints.filter(([v]) => !substitutions[v]); // Return the deepest level the variable is found in, or 0 if not found. function maxLevelVariableIsContained( @@ -360,9 +364,9 @@ function findSubstitutions(constraints: Constraint[]): Substitutions { // If the variable isn't mentioned on the right side, always substitute it constraints.forEach(([variable, expression]) => { const level = maxLevelVariableIsContained(variable, expression); - if (level === 0) substitutions[variable] = expression; + if (level === 0) newSubstitutions[variable] = expression; }); - if (Object.keys(substitutions).length > 0) return substitutions; + if (Object.keys(newSubstitutions).length > 0) return newSubstitutions; // Split unique from non-unique constraints const uniqueConstraints: Constraint[] = []; @@ -390,12 +394,15 @@ function findSubstitutions(constraints: Constraint[]): Substitutions { "join" | "meet", PrincipalExpression[] ]; - substitutions[variable] = [op, expressions.filter((e) => e !== variable)]; + newSubstitutions[variable] = [ + op, + expressions.filter((e) => e !== variable), + ]; } }); - if (Object.keys(substitutions).length > 0) return substitutions; + if (Object.keys(newSubstitutions).length > 0) return newSubstitutions; - return substitutions; + return {}; } function applySubstitutions( @@ -428,14 +435,16 @@ function unify(constraints: Constraint[], lattice: Lattice): Constraint[] { } let substitutions: { [key: string]: PrincipalExpression } = {}; + let newSubstitutions: { [key: string]: PrincipalExpression } = {}; do { constraints = constraints.map(([v, e]) => [v, traverse(e)]); - substitutions = findSubstitutions(constraints); + newSubstitutions = findSubstitutions(constraints, substitutions); constraints = constraints.map(([v, e]) => [ v, - applySubstitutions(e, substitutions), + applySubstitutions(e, newSubstitutions), ]); - } while (Object.keys(substitutions).length > 0); + substitutions = { ...substitutions, ...newSubstitutions }; + } while (Object.keys(newSubstitutions).length > 0); return constraints; } diff --git a/typescript/packages/basic-ifc/src/ifc_test.ts b/typescript/packages/basic-ifc/src/ifc_test.ts index 022cfa421..b4391e749 100644 --- a/typescript/packages/basic-ifc/src/ifc_test.ts +++ b/typescript/packages/basic-ifc/src/ifc_test.ts @@ -108,7 +108,7 @@ Deno.test("generate constraints", () => { ]); }); -Deno.test("infer labels", () => { +Deno.test("infer labels, simple", () => { // Example state and bindings const initialState: State = { bar: { @@ -133,5 +133,66 @@ Deno.test("infer labels", () => { const inferredState = inferLabels(initialState, bindings, lattice); - assertEquals(inferredState, {}); + assertEquals(inferredState, { + bar: { + baz: { + [$label]: { + integrity: "trusted cloud", + confidentiality: "trusted cloud", + }, + }, + zab: { + [$label]: { integrity: "public", confidentiality: "public" }, + }, + }, + foo: { + [$label]: { integrity: "public", confidentiality: "trusted cloud" }, + }, + }); +}); + +Deno.test("infer labels, two nodes", () => { + // Example state and bindings + const inputs: State = { + bar: { + baz: { + [$label]: { + integrity: "trusted cloud", + confidentiality: "trusted cloud", + }, + }, + zab: { + [$label]: { integrity: "public", confidentiality: "public" }, + }, + }, + }; + const bindings: Node[] = [ + { in: ["bar.baz", "bar.zab"], out: ["foo"] }, + { in: ["foo", "bar.zab"], out: ["zab"] }, + ]; + + const lattice = makeLattice({ + public: ["trusted cloud"], + "trusted cloud": ["cc", "openai", "anthropic"], + cc: ["ondevice"], + }); + + const inferredState = inferLabels(inputs, bindings, lattice); + + assertEquals(inferredState, { + bar: { + baz: { + [$label]: { + integrity: "trusted cloud", + confidentiality: "trusted cloud", + }, + }, + zab: { + [$label]: { integrity: "public", confidentiality: "public" }, + }, + }, + foo: { + [$label]: { integrity: "public", confidentiality: "trusted cloud" }, + }, + }); }); From c0e54909efefc412859527e6dca982762409941e Mon Sep 17 00:00:00 2001 From: Bernhard Seefeld Date: Tue, 4 Jun 2024 12:23:26 -0700 Subject: [PATCH 7/8] added simplify expression (doesn't yet fix second test) --- typescript/packages/basic-ifc/src/ifc.ts | 109 ++++++++++++++++-- typescript/packages/basic-ifc/src/ifc_test.ts | 50 ++++---- 2 files changed, 126 insertions(+), 33 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index 87fc9cd74..552504daf 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -364,7 +364,8 @@ function findSubstitutions( // If the variable isn't mentioned on the right side, always substitute it constraints.forEach(([variable, expression]) => { const level = maxLevelVariableIsContained(variable, expression); - if (level === 0) newSubstitutions[variable] = expression; + if (level === 0) + newSubstitutions[variable] = simplifyExpression(expression); }); if (Object.keys(newSubstitutions).length > 0) return newSubstitutions; @@ -384,9 +385,9 @@ function findSubstitutions( } }); - // If there is only one constraint for a variable, we can substitute it if - // the variable is only mentioned in the first layer. - uniqueConstraints.forEach(([variable, expression]) => { + function substituteWithoutSelfReference([variable, expression]: Constraint): + | PrincipalExpression + | undefined { const level = maxLevelVariableIsContained(variable, expression); // level 2 means it's $var = [ "meet"| "join", [$var, ...] ] if (level == 2) { @@ -394,10 +395,39 @@ function findSubstitutions( "join" | "meet", PrincipalExpression[] ]; - newSubstitutions[variable] = [ + return simplifyExpression([ op, expressions.filter((e) => e !== variable), + ]); + } + return undefined; + } + + // If there is only one constraint for a variable, we can substitute it if + // the variable is only mentioned in the first layer. + uniqueConstraints.forEach(([v, e]) => { + const substitution = substituteWithoutSelfReference([v, e]); + if (substitution) newSubstitutions[v] = substitution; + }); + if (Object.keys(newSubstitutions).length > 0) return newSubstitutions; + + // If there are multiple constraints for a variable, and one has the variable + // at the top level, substitute all others into it via the same join/meet, + // a = a v (b ^ c), a = a v (d ^ e) => a = a v (d ^ e) v (b ^ c ^ d ^ e) + multipleConstraints.forEach((expressions, variable) => { + const container = expressions.find( + (e) => maxLevelVariableIsContained(variable, e) === 2 + ); + + if (container && isCombinedPrincipal(container)) { + const newExpression: PrincipalExpression = [ + container[0], + [...container[1], ...expressions.filter((e) => e !== container)], ]; + newSubstitutions[variable] = simplifyExpression( + substituteWithoutSelfReference([variable, newExpression]) ?? + newExpression + ); } }); if (Object.keys(newSubstitutions).length > 0) return newSubstitutions; @@ -419,6 +449,63 @@ function applySubstitutions( } } +// Finds nested join and meet and flattens them into one +function simplifyExpression( + expression: PrincipalExpression +): PrincipalExpression { + if (isCombinedPrincipal(expression)) { + const [op, expressions] = expression; + const simplified: PrincipalExpression[] = []; + expressions.forEach((e) => { + if (isCombinedPrincipal(e)) { + if (e[0] === op) { + const flatten = (e: PrincipalExpression): PrincipalExpression[] => { + if (isCombinedPrincipal(e) && e[0] === op) { + return e[1].flatMap(flatten); + } else { + return [e]; + } + }; + + simplified.push(...flatten(e)); + } else { + // The opposite of `op`, so let's see whether we can swap it. + // Let's handle `B ^ (B v C)` <=> `B v (B ^ C)` for now. + // B AND (B OR C) AND D <=> B OR (B AND C) OR (B AND D) + const single = e[1].find((e) => !isCombinedPrincipal(e)); + const combined = e[1].find( + (e) => isCombinedPrincipal(e) && e[0] === op + ) as ["join" | "meet", PrincipalExpression[]] | undefined; + if ( + e[1].length === 2 && + single && + combined && + single !== combined && + combined[1].includes(single) + ) { + // We can commute the operation and so flatten it by one level + simplified.push(single); + simplified.push([e[0], combined[1]]); + console.log("commuted", single, combined); + } else { + simplified.push(simplifyExpression(e)); + } + } + } else { + simplified.push(e); + } + }); + + const deduped = dedupe(simplified); + if (deduped.length === 1) console.log("single expression", deduped[0]); + return deduped.length === 1 + ? simplifyExpression(deduped[0]) + : [op, deduped]; + } else { + return expression; + } +} + function unify(constraints: Constraint[], lattice: Lattice): Constraint[] { function traverse(expression: PrincipalExpression): PrincipalExpression { if (isLatticeVariable(expression)) { @@ -434,19 +521,21 @@ function unify(constraints: Constraint[], lattice: Lattice): Constraint[] { } } - let substitutions: { [key: string]: PrincipalExpression } = {}; - let newSubstitutions: { [key: string]: PrincipalExpression } = {}; + let substitutions: { [key: PrincipalVariable]: PrincipalExpression } = {}; + let newSubstitutions: { [key: PrincipalVariable]: PrincipalExpression } = {}; do { - constraints = constraints.map(([v, e]) => [v, traverse(e)]); + constraints = constraints + .filter(([v]) => !substitutions[v]) + .map(([v, e]) => [v, traverse(e)]); newSubstitutions = findSubstitutions(constraints, substitutions); constraints = constraints.map(([v, e]) => [ v, - applySubstitutions(e, newSubstitutions), + simplifyExpression(applySubstitutions(e, newSubstitutions)), ]); substitutions = { ...substitutions, ...newSubstitutions }; } while (Object.keys(newSubstitutions).length > 0); - return constraints; + return [...(Object.entries(substitutions) as Constraint[]), ...constraints]; } function inferLabels( diff --git a/typescript/packages/basic-ifc/src/ifc_test.ts b/typescript/packages/basic-ifc/src/ifc_test.ts index b4391e749..ad5be061f 100644 --- a/typescript/packages/basic-ifc/src/ifc_test.ts +++ b/typescript/packages/basic-ifc/src/ifc_test.ts @@ -1,4 +1,7 @@ -import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { + assertEquals, + assertObjectMatch, +} from "https://deno.land/std@0.224.0/assert/mod.ts"; import { inferLabels, generateConstraints, @@ -109,44 +112,44 @@ Deno.test("generate constraints", () => { }); Deno.test("infer labels, simple", () => { - // Example state and bindings - const initialState: State = { - bar: { - baz: { - [$label]: { - integrity: "trusted cloud", - confidentiality: "trusted cloud", - }, - }, - zab: { - [$label]: { integrity: "public", confidentiality: "public" }, - }, - }, - }; - const bindings: Node[] = [{ in: ["bar.baz", "bar.zab"], out: ["foo"] }]; - + // Example lattice: Each line is a principal, listing its parents const lattice = makeLattice({ public: ["trusted cloud"], "trusted cloud": ["cc", "openai", "anthropic"], cc: ["ondevice"], + "possible prompt injection": ["user authored"], }); - const inferredState = inferLabels(initialState, bindings, lattice); - - assertEquals(inferredState, { + // Example inputs to the recipe, with labels at any level in the tree + const inputs = { bar: { baz: { [$label]: { - integrity: "trusted cloud", + integrity: "user authored", confidentiality: "trusted cloud", }, }, zab: { - [$label]: { integrity: "public", confidentiality: "public" }, + [$label]: { + integrity: "possible prompt injection", + confidentiality: "public", + }, }, }, + }; + + // The recipe + const bindings = [{ type: "foo", in: ["bar.baz", "bar.zab"], out: ["foo"] }]; + + const inferredLabels = inferLabels(inputs, bindings, lattice); + console.log(inferredLabels); + + assertObjectMatch(inferredLabels, { foo: { - [$label]: { integrity: "public", confidentiality: "trusted cloud" }, + [$label]: { + integrity: "possible prompt injection", + confidentiality: "trusted cloud", + }, }, }); }); @@ -179,6 +182,7 @@ Deno.test("infer labels, two nodes", () => { const inferredState = inferLabels(inputs, bindings, lattice); + console.log(inferredState); assertEquals(inferredState, { bar: { baz: { From f2c6b339d730e2b61335b0975c06911291a2df1a Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Tue, 4 Jun 2024 14:10:50 -0700 Subject: [PATCH 8/8] * Reduce loops in computing parent labels during Lattice creation. * Remove duplicate terminal labels (TOP, BOTTOM) in relationship lattice. --- typescript/packages/basic-ifc/src/ifc.ts | 44 +++++++++++------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/typescript/packages/basic-ifc/src/ifc.ts b/typescript/packages/basic-ifc/src/ifc.ts index 552504daf..e629e9046 100644 --- a/typescript/packages/basic-ifc/src/ifc.ts +++ b/typescript/packages/basic-ifc/src/ifc.ts @@ -66,7 +66,10 @@ function computeAllParentsForLabel( if (label === end) { return []; } - const parents = lattice[label] || [end]; + const parents = lattice[label]; + if (!parents) { + return []; + } return parents.reduce( (acc, parent) => [ ...acc, @@ -99,25 +102,18 @@ function makeLattice(latticeRelationships: LatticeRelationships): Lattice { }; // Compute all parents and children for each key - for (const key in latticeRelationships) + for (const key of allKeys) { lattice.parents[key] = [ key, ...computeAllParentsForLabel(key, latticeRelationships, TOP), + TOP, ]; - for (const key in invertedLattice) lattice.children[key] = [ key, ...computeAllParentsForLabel(key, invertedLattice, BOTTOM), + BOTTOM, ]; - - for (const key of allKeys) { - if (!lattice.parents[key]) { - lattice.parents[key] = [key, TOP]; - } - if (!lattice.children[key]) { - lattice.children[key] = [key, BOTTOM]; - } } return lattice; @@ -153,12 +149,12 @@ function join( return otherPrincipals.length ? ([ - "join", - [ - ...otherPrincipals, - ...(commonParents.length ? [commonParents[0]] : []), - ], - ] as PrincipalExpression) + "join", + [ + ...otherPrincipals, + ...(commonParents.length ? [commonParents[0]] : []), + ], + ] as PrincipalExpression) : commonParents[0] ?? TOP; } @@ -181,12 +177,12 @@ function meet( return otherPrincipals.length ? ([ - "meet", - [ - ...otherPrincipals, - ...(commonChildren.length ? [commonChildren[0]] : []), - ], - ] as PrincipalExpression) + "meet", + [ + ...otherPrincipals, + ...(commonChildren.length ? [commonChildren[0]] : []), + ], + ] as PrincipalExpression) : commonChildren[0] ?? BOTTOM; } @@ -426,7 +422,7 @@ function findSubstitutions( ]; newSubstitutions[variable] = simplifyExpression( substituteWithoutSelfReference([variable, newExpression]) ?? - newExpression + newExpression ); } });