diff --git a/Cargo.lock b/Cargo.lock index 8acca483..9a1a1517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,9 +304,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -315,9 +315,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" dependencies = [ "cc", "cmake", @@ -702,9 +702,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", @@ -1172,9 +1172,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.11.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "400a21f1014a968ec518c7ccdf9b4a4ed0cac8c56ccb6d604f8b91f00110501e" +checksum = "6d765eb1c0bda10d31e0ea185f5ee15da532d60b0912d2bd1441783439e749c5" [[package]] name = "ctr" @@ -1229,7 +1229,7 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest 0.11.2", + "digest 0.11.3", "fiat-crypto 0.3.0", "rustc_version", "subtle", @@ -1316,9 +1316,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "const-oid 0.10.2", @@ -1362,10 +1362,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91bbdd377139884fafcad8dc43a760a3e1e681aa26db910257fa6535b70e1829" dependencies = [ "der 0.8.0", - "digest 0.11.2", + "digest 0.11.3", "elliptic-curve", "rfc6979", - "signature 3.0.0-rc.10", + "signature 3.0.0", "spki 0.8.0-rc.4", "zeroize", ] @@ -1387,7 +1387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" dependencies = [ "pkcs8 0.11.0-rc.11", - "signature 3.0.0-rc.10", + "signature 3.0.0", ] [[package]] @@ -1416,7 +1416,7 @@ dependencies = [ "rand_core 0.10.1", "serde", "sha2 0.11.0", - "signature 3.0.0-rc.10", + "signature 3.0.0", "subtle", "zeroize", ] @@ -1436,7 +1436,7 @@ dependencies = [ "base16ct", "crypto-bigint", "crypto-common 0.2.1", - "digest 0.11.2", + "digest 0.11.3", "hkdf", "hybrid-array", "once_cell", @@ -1891,9 +1891,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heapless" @@ -1963,7 +1963,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -2016,9 +2016,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "ctutils", "subtle", @@ -2224,7 +2224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -2283,7 +2283,7 @@ dependencies = [ "sec1", "sha1 0.11.0", "sha2 0.11.0", - "signature 3.0.0-rc.10", + "signature 3.0.0", "ssh-cipher", "ssh-encoding", "subtle", @@ -2323,16 +2323,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is-macro" version = "0.3.7" @@ -2539,9 +2529,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -2740,7 +2730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" dependencies = [ "cfg-if", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -2822,9 +2812,9 @@ dependencies = [ [[package]] name = "module-lattice" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7c90d33a0dac244570c26461d761ffaeadb3bfc2b17cc625ae2185cafdffae" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" dependencies = [ "ctutils", "hybrid-array", @@ -2862,9 +2852,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.8.6" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e55037284865448ecf329baa86a4d05401f647ebde99f5747b640d32c2c5226" +checksum = "f1d395473824516f38dd1071a1a37bc57daa7be65b293ebba4ead5f7abb017a2" dependencies = [ "bitflags", "ctor", @@ -2878,15 +2868,15 @@ dependencies = [ [[package]] name = "napi-build" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d376940fd5b723c6893cd1ee3f33abbfd86acb1cd1ec079f3ab04a2a3bc4d3b1" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" [[package]] name = "napi-derive" -version = "3.5.5" +version = "3.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ba740fe4c9524d86fd90798fd8ccdb23402b3eef7e7c30897a8a369b529fcf" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" dependencies = [ "convert_case", "ctor", @@ -2929,9 +2919,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags", "cfg-if", @@ -3319,9 +3309,9 @@ dependencies = [ [[package]] name = "pack1" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6e7cd9bd638dc2c831519a0caa1c006cab771a92b1303403a8322773c5b72d6" +checksum = "e3b7bb0ecf2e447b1f20ee94ee79ef6eed1e9d4b3c36ce1903b9dea3bf205523" dependencies = [ "bytemuck", ] @@ -3422,7 +3412,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", "hmac 0.13.0", ] @@ -4189,9 +4179,9 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.5.0-rc.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a3127ee32baec36af75b4107082d9bd823501ec14a4e016be4b6b37faa74ae" +checksum = "5236ce872cac07e0fb3969b0cbf468c7d2f37d432f1b627dcb7b8d34563fb0c3" dependencies = [ "hmac 0.13.0", "subtle", @@ -4230,12 +4220,12 @@ dependencies = [ "const-oid 0.10.2", "crypto-bigint", "crypto-primes", - "digest 0.11.2", + "digest 0.11.3", "pkcs1", "pkcs8 0.11.0-rc.11", "rand_core 0.10.1", "sha2 0.11.0", - "signature 3.0.0-rc.10", + "signature 3.0.0", "spki 0.8.0-rc.4", "zeroize", ] @@ -4317,9 +4307,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.60.2" +version = "0.60.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e358980fe9b079b99da387117864ee6f0a3fd02f39e5b5fde6af9c2895374" +checksum = "324b92f459d3e42da294e14e8eb150d2215fcfb7c966838bc1127cd68bc05a0d" dependencies = [ "aead 0.6.0-rc.10", "aes 0.8.4", @@ -4386,7 +4376,7 @@ dependencies = [ "sha2 0.10.9", "sha2 0.11.0", "sha3", - "signature 3.0.0-rc.10", + "signature 3.0.0", "spki 0.8.0-rc.4", "ssh-encoding", "subtle", @@ -4399,9 +4389,9 @@ dependencies = [ [[package]] name = "russh-cryptovec" -version = "0.59.0" +version = "0.60.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36140e8a20297bc2e8338807c3d9ca911f7fa49d7539cbcd6d48d3befd70efd8" +checksum = "37cb4d0360bdd8935392a306d8b5edb539cc455b30e8bf13dd213a0cf7879b40" dependencies = [ "log", "nix", @@ -4811,9 +4801,9 @@ dependencies = [ [[package]] name = "serdect" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" dependencies = [ "base16ct", "serde", @@ -4864,7 +4854,7 @@ checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -4892,7 +4882,7 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -4901,7 +4891,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", "keccak", ] @@ -4971,11 +4961,11 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.10" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", "rand_core 0.10.1", ] @@ -5018,9 +5008,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -5517,20 +5507,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -5968,9 +5958,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -5981,9 +5971,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.70" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ "js-sys", "wasm-bindgen", @@ -5991,9 +5981,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6001,9 +5991,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -6014,9 +6004,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -6070,9 +6060,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.97" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -6312,9 +6302,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" [[package]] name = "wit-bindgen" @@ -6493,9 +6483,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] diff --git a/README.md b/README.md index e4d9ab69..8b2bc747 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Homepage: [bashkit.sh](https://bashkit.sh) - **Secure by default** - No process spawning, no filesystem access, no network access unless explicitly enabled. [250+ threats](specs/threat-model.md) analyzed and mitigated - **POSIX compliant** - Substantial IEEE 1003.1-2024 Shell Command Language compliance -- **Sandboxed, in-process execution** - All 160 commands reimplemented in Rust, no `fork`/`exec` +- **Sandboxed, in-process execution** - All 156 commands reimplemented in Rust, no `fork`/`exec` - **Virtual filesystem** - InMemoryFs, OverlayFs, MountableFs with optional RealFs backend (`realfs` feature) - **Resource limits** - Command count, loop iterations, function depth, output size, filesystem size, parser fuel - **Network allowlist** - HTTP access denied by default, per-domain control @@ -113,7 +113,7 @@ assert_eq!(output.result["stdout"], "hello\nworld\n"); -## Built-in Commands (160) +## Built-in Commands (156) | Category | Commands | |----------|----------| @@ -468,7 +468,7 @@ Bashkit is built for running untrusted scripts from AI agents and users. Securit | Layer | Protection | |-------|------------| -| **No process spawning** | All 160 commands are reimplemented in Rust — no `fork`, `exec`, or shell escape | +| **No process spawning** | All 156 commands are reimplemented in Rust — no `fork`, `exec`, or shell escape | | **Virtual filesystem** | Scripts see an in-memory FS by default; no host filesystem access unless explicitly mounted | | **Network allowlist** | HTTP access is denied by default; each domain must be explicitly allowed | | **Resource limits** | Configurable caps on commands (10K), loop iterations (100K), function depth (100), output (10MB), input (10MB) | diff --git a/crates/bashkit-python/README.md b/crates/bashkit-python/README.md index 939459ac..d2843f6e 100644 --- a/crates/bashkit-python/README.md +++ b/crates/bashkit-python/README.md @@ -10,7 +10,7 @@ Homepage: [bashkit.sh](https://bashkit.sh) - Sandboxed execution in-process, without containers or subprocess orchestration - Full bash syntax: variables, pipelines, redirects, loops, functions, and arrays -- 160 built-in commands including `grep`, `sed`, `awk`, `jq`, `curl`, and `find` +- 156 built-in commands including `grep`, `sed`, `awk`, `jq`, `curl`, and `find` - Persistent interpreter state across calls, including variables, cwd, and VFS contents - Direct virtual filesystem APIs, constructor mounts, and live host mounts - Snapshot and restore support on `Bash` and `BashTool` diff --git a/crates/bashkit/docs/compatibility.md b/crates/bashkit/docs/compatibility.md index 8dd251ba..71ce2f81 100644 --- a/crates/bashkit/docs/compatibility.md +++ b/crates/bashkit/docs/compatibility.md @@ -29,18 +29,11 @@ for sandbox security reasons. See the compliance spec for details. ## Quick Status -| Category | Count | -|----------|-------| -| Core & Navigation | 12 | -| Flow Control & Variables | 23 | -| Shell | 7 | -| Text Processing | 20 | -| File Operations & Inspection | 17 | -| Archives & Byte Tools | 6 | -| Utilities & System | 20 | -| Network | 2 | -| Experimental | 3 | -| **Total** | **150** | +| Group | Count | +|-------|-------| +| Always-on builtins | 142 | +| Feature-gated builtins (`jq`, `git`, `ssh`/`scp`/`sftp`, `python`/`python3`, `sqlite`/`sqlite3`, `ts`/`typescript`/`node`/`deno`/`bun`) | 14 | +| **Total** | **156** | --- diff --git a/crates/bashkit/docs/threat-model.md b/crates/bashkit/docs/threat-model.md index 63ba886c..5624b52e 100644 --- a/crates/bashkit/docs/threat-model.md +++ b/crates/bashkit/docs/threat-model.md @@ -215,7 +215,7 @@ Scripts may attempt to leak sensitive information. | IP address (TM-INF-007) | `ip addr`, `ifconfig` | Not implemented | MITIGATED | | System info (TM-INF-008) | `uname -a` | Returns configurable virtual values | MITIGATED | | User ID (TM-INF-009) | `id` | Returns hardcoded uid=1000 | MITIGATED | -| Date/time (TM-INF-018) | `date` | Returns real host time (fingerprinting risk) | **OPEN** | +| Date/time (TM-INF-018) | `date` | Returns real host time (fingerprinting risk) | **MITIGATED** (opt-in: `Bash::builder().fixed_epoch` / `.epoch_offset`) | **Network Exfiltration:** diff --git a/crates/bashkit/src/builtins/date.rs b/crates/bashkit/src/builtins/date.rs index d8286698..59e8686a 100644 --- a/crates/bashkit/src/builtins/date.rs +++ b/crates/bashkit/src/builtins/date.rs @@ -46,26 +46,53 @@ use crate::interpreter::ExecResult; /// %n Newline /// %t Tab /// %% Literal % -/// THREAT[TM-INF-018]: Supports a fixed epoch to prevent leaking real host time. +/// THREAT[TM-INF-018]: Supports a fixed epoch OR a constant offset on +/// the real clock so callers can blind absolute wall-clock time without +/// breaking elapsed-time logic. The two modes are mutually exclusive +/// — `fixed_epoch` wins if both are set. pub struct Date { /// Fixed UTC epoch for virtualized time. None = use real system clock. fixed_epoch: Option>, + /// Constant offset applied to `Utc::now()` when `fixed_epoch` is None. + offset_seconds: i64, } impl Date { pub fn new() -> Self { - Self { fixed_epoch: None } + Self { + fixed_epoch: None, + offset_seconds: 0, + } } /// Create a Date builtin with a fixed epoch (for sandboxing). pub fn with_fixed_epoch(epoch: DateTime) -> Self { Self { fixed_epoch: Some(epoch), + offset_seconds: 0, + } + } + + /// Create a Date builtin that offsets the real clock by the given + /// number of seconds. Useful when scripts need a ticking clock but + /// must not observe the host's exact wall-clock time. + pub fn with_offset_seconds(offset: i64) -> Self { + Self { + fixed_epoch: None, + offset_seconds: offset, } } fn now(&self) -> DateTime { - self.fixed_epoch.unwrap_or_else(Utc::now) + if let Some(t) = self.fixed_epoch { + return t; + } + if self.offset_seconds == 0 { + return Utc::now(); + } + Utc::now() + .checked_add_signed(chrono::Duration::seconds(self.offset_seconds)) + .unwrap_or_else(Utc::now) } } @@ -535,6 +562,55 @@ mod tests { assert!(result.stdout.len() > 10); } + /// TM-INF-018: fixed_epoch wins over real clock. + #[test] + fn date_fixed_epoch_returns_fixed_time() { + let fixed = DateTime::::from_timestamp(1_700_000_000, 0).unwrap(); + let d = Date::with_fixed_epoch(fixed); + assert_eq!(d.now(), fixed); + } + + /// TM-INF-018: non-zero offset shifts the real clock without + /// freezing it. Verify the offset is applied within a sub-second + /// tolerance vs `Utc::now() + offset`. + #[test] + fn date_offset_seconds_shifts_real_clock() { + let offset: i64 = 365 * 24 * 60 * 60; // +1 year + let d = Date::with_offset_seconds(offset); + let before = Utc::now(); + let observed = d.now(); + let after = Utc::now(); + let expected_low = before + chrono::Duration::seconds(offset); + let expected_high = after + chrono::Duration::seconds(offset); + assert!( + observed >= expected_low && observed <= expected_high, + "offset clock {observed} not in [{expected_low}, {expected_high}]" + ); + } + + /// TM-INF-018: fixed_epoch takes priority if both modes are set + /// (defensive — the builder enforces exclusivity but the struct + /// fields are pub(crate)-ish and could be combined directly). + #[test] + fn date_fixed_epoch_overrides_offset() { + let fixed = DateTime::::from_timestamp(1_700_000_000, 0).unwrap(); + let d = Date { + fixed_epoch: Some(fixed), + offset_seconds: 99_999, + }; + assert_eq!(d.now(), fixed); + } + + /// TM-INF-018: zero offset = real clock (no allocation overhead path). + #[test] + fn date_zero_offset_uses_real_clock() { + let d = Date::with_offset_seconds(0); + let before = Utc::now(); + let observed = d.now(); + let after = Utc::now(); + assert!(observed >= before && observed <= after); + } + #[tokio::test] async fn test_date_format_year() { let result = run_date(&["+%Y"]).await; diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index 923de611..3d908a3f 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -878,7 +878,15 @@ impl Interpreter { /// Create a new interpreter with the given filesystem. pub fn new(fs: Arc) -> Self { - Self::with_config(fs, None, None, None, HashMap::new(), ShellProfile::Full) + Self::with_config( + fs, + None, + None, + None, + None, + HashMap::new(), + ShellProfile::Full, + ) } /// Create a new interpreter with custom username, hostname, and builtins. @@ -894,6 +902,7 @@ impl Interpreter { username: Option, hostname: Option, fixed_epoch: Option, + epoch_offset: Option, custom_builtins: HashMap>, shell_profile: ShellProfile, ) -> Self { @@ -1076,7 +1085,8 @@ impl Interpreter { ); builtins.insert(".".to_string(), Box::new(builtins::Source::new(fs.clone()))); - // THREAT[TM-INF-018]: Use fixed epoch if configured, else real clock + // THREAT[TM-INF-018]: Resolve the virtual clock mode for `date`. + // Priority: fixed_epoch > epoch_offset > real clock. builtins.insert( "date".to_string(), Box::new(if let Some(epoch) = fixed_epoch { @@ -1084,6 +1094,8 @@ impl Interpreter { builtins::Date::with_fixed_epoch( DateTime::from_timestamp(epoch, 0).unwrap_or_default(), ) + } else if let Some(offset) = epoch_offset { + builtins::Date::with_offset_seconds(offset) } else { builtins::Date::new() }), diff --git a/crates/bashkit/src/lib.rs b/crates/bashkit/src/lib.rs index 67f1f316..8270b9c5 100644 --- a/crates/bashkit/src/lib.rs +++ b/crates/bashkit/src/lib.rs @@ -18,7 +18,7 @@ //! - **Experimental: Python** - Embedded Python via [Monty](https://github.com/pydantic/monty) (`python` feature) //! - **Experimental: SQLite** - Embedded SQLite-compatible engine via [Turso](https://github.com/tursodatabase/turso) (`sqlite` feature) //! -//! # Built-in Commands (160) +//! # Built-in Commands (156) //! //! | Category | Commands | //! |----------|----------| @@ -1209,6 +1209,8 @@ pub struct BashBuilder { hostname: Option, /// Fixed epoch for virtualizing the `date` builtin (TM-INF-018) fixed_epoch: Option, + /// Constant seconds offset applied to real-clock for `date` (TM-INF-018) + epoch_offset: Option, shell_profile: interpreter::ShellProfile, custom_builtins: HashMap>, /// Files to mount in the virtual filesystem @@ -1397,6 +1399,24 @@ impl BashBuilder { /// When set, `date` returns this fixed time instead of the real clock. pub fn fixed_epoch(mut self, epoch: i64) -> Self { self.fixed_epoch = Some(epoch); + self.epoch_offset = None; + self + } + + /// Apply a constant offset (in seconds) to the real system clock for + /// the `date` builtin. Use this when scripts need time to advance at + /// real-clock rate but you want to obscure the absolute wall-clock + /// time from the sandbox (timing-correlation resistance). + /// + /// THREAT[TM-INF-018]: A non-zero offset prevents `date` from + /// exposing the host's exact wall-clock time while still letting + /// time-sensitive scripts observe elapsed-time deltas. + /// + /// `fixed_epoch` and `epoch_offset` are mutually exclusive — the + /// last builder call wins. + pub fn epoch_offset(mut self, seconds: i64) -> Self { + self.epoch_offset = Some(seconds); + self.fixed_epoch = None; self } @@ -2430,6 +2450,7 @@ impl BashBuilder { self.username, self.hostname, self.fixed_epoch, + self.epoch_offset, self.cwd, self.shell_profile, self.limits, @@ -2681,6 +2702,7 @@ impl BashBuilder { username: Option, hostname: Option, fixed_epoch: Option, + epoch_offset: Option, cwd: Option, shell_profile: interpreter::ShellProfile, limits: ExecutionLimits, @@ -2714,6 +2736,7 @@ impl BashBuilder { username.clone(), hostname, fixed_epoch, + epoch_offset, custom_builtins, shell_profile, ); diff --git a/crates/bashkit/tests/threat_model_tests.rs b/crates/bashkit/tests/threat_model_tests.rs index 80299aec..36164d9c 100644 --- a/crates/bashkit/tests/threat_model_tests.rs +++ b/crates/bashkit/tests/threat_model_tests.rs @@ -3772,6 +3772,73 @@ echo "done" } } +// ============================================================================= +// DATE / VIRTUAL CLOCK TESTS (TM-INF-018) +// ============================================================================= + +mod tm_inf_018_date { + use super::*; + + /// TM-INF-018: `Bash::builder().fixed_epoch(N)` causes `date +%s` + /// to return exactly N, regardless of host wall-clock. + #[tokio::test] + async fn fixed_epoch_freezes_date() { + let mut bash = Bash::builder().fixed_epoch(1_700_000_000).build(); + let r = bash.exec("date +%s").await.unwrap(); + assert_eq!(r.exit_code, 0); + assert_eq!(r.stdout.trim(), "1700000000"); + } + + /// TM-INF-018: `Bash::builder().epoch_offset(N)` keeps the clock + /// ticking but shifts its absolute value by N seconds. Verify two + /// consecutive reads differ by less than 1s (ticking) yet sit at + /// least N-1 seconds ahead of host real time (offset applied). + #[tokio::test] + async fn epoch_offset_shifts_real_clock() { + let offset = 365_i64 * 24 * 3600; // +1 year + let host_before = chrono::Utc::now().timestamp(); + let mut bash = Bash::builder().epoch_offset(offset).build(); + let r = bash.exec("date +%s").await.unwrap(); + assert_eq!(r.exit_code, 0); + let observed: i64 = r.stdout.trim().parse().unwrap(); + // observed should be ~ host_before + offset + let delta = observed - (host_before + offset); + assert!( + (-2..=2).contains(&delta), + "offset clock drifted: observed={observed}, expected≈{}, delta={delta}", + host_before + offset + ); + } + + /// TM-INF-018: `fixed_epoch` and `epoch_offset` are mutually + /// exclusive — last builder call wins. fixed_epoch followed by + /// epoch_offset should disable fixed_epoch. + #[tokio::test] + async fn last_builder_call_wins_offset_after_fixed() { + let mut bash = Bash::builder().fixed_epoch(0).epoch_offset(0).build(); + let r = bash.exec("date +%s").await.unwrap(); + let observed: i64 = r.stdout.trim().parse().unwrap(); + // Should be near real-time (within a few seconds), not 0. + let now = chrono::Utc::now().timestamp(); + assert!( + (observed - now).abs() < 60, + "epoch_offset(0) did not override fixed_epoch(0): observed={observed}, real={now}" + ); + } + + /// TM-INF-018: the inverse — epoch_offset then fixed_epoch should + /// disable the offset. + #[tokio::test] + async fn last_builder_call_wins_fixed_after_offset() { + let mut bash = Bash::builder() + .epoch_offset(99_999) + .fixed_epoch(1_700_000_000) + .build(); + let r = bash.exec("date +%s").await.unwrap(); + assert_eq!(r.stdout.trim(), "1700000000"); + } +} + // ============================================================================= // TRACE EVENT TESTS (TM-INF-019) // ============================================================================= diff --git a/justfile b/justfile index 9578ad41..7340285e 100644 --- a/justfile +++ b/justfile @@ -184,16 +184,21 @@ eval-scripting-save dataset="crates/bashkit-eval/data/scripting-tool/many-tools. # === Security === +# Auto-install cargo-vet if missing (idempotent, matches CI's +# taiki-e/install-action step). Internal helper for vet recipes. +_ensure-vet: + @command -v cargo-vet >/dev/null 2>&1 || cargo install cargo-vet --locked + # Run supply chain audit (cargo-vet) -vet: - cargo vet +vet: _ensure-vet + cargo vet --locked # Suggest crates to audit -vet-suggest: +vet-suggest: _ensure-vet cargo vet suggest # Certify a crate after audit -vet-certify crate version: +vet-certify crate version: _ensure-vet cargo vet certify {{crate}} {{version}} # === Nightly CI === diff --git a/specs/implementation-status.md b/specs/implementation-status.md index 54da757f..5a1ba0a5 100644 --- a/specs/implementation-status.md +++ b/specs/implementation-status.md @@ -279,7 +279,7 @@ Features that may be added in the future (not intentionally excluded): ### Implemented -**148 core builtins + 14 feature-gated = 162 total** +**142 core builtins + 14 feature-gated = 156 total** `echo`, `printf`, `cat`, `nl`, `cd`, `pwd`, `true`, `false`, `exit`, `test`, `[`, `export`, `set`, `unset`, `local`, `source`, `.`, `read`, `shift`, `break`, diff --git a/specs/maintenance.md b/specs/maintenance.md index 648aee41..1b1f1366 100644 --- a/specs/maintenance.md +++ b/specs/maintenance.md @@ -187,9 +187,14 @@ multi-file refactors, cross-cutting changes), the pass must: Deferred items are **not** failures — they are expected for large-scope improvements. The requirement is that they are **tracked**, not silently skipped. -_No deferred items currently outstanding. Previously tracked items -(#880 ArgParser migration, #881 errexit propagation helper) have been -resolved._ +### Deferred from 2026-05-17 run + +| Issue | Section | Description | +|-------|---------|-------------| +| #1634 | Dependencies | RustCrypto stack split between 0.10 and 0.11 lines, blocked on `turso_core` / `aes-gcm` upstreams | + +Previously tracked items (#880 ArgParser migration, #881 errexit +propagation helper) have been resolved. ## Automation diff --git a/specs/threat-model.md b/specs/threat-model.md index 80297362..73c7b3f9 100644 --- a/specs/threat-model.md +++ b/specs/threat-model.md @@ -1330,7 +1330,7 @@ This section maps former vulnerability IDs to the new threat ID scheme and track | ~~TM-INJ-015~~ | ~~`export` bypasses `is_internal_variable()`~~ | ~~Internal prefix injection via export~~ | ~~Add `is_internal_variable()` check~~ (**FIXED**) | | ~~TM-INJ-016~~ | ~~`_ARRAY_READ_` prefix not in `is_internal_variable()`~~ | ~~Arbitrary array creation/overwrite via marker injection~~ | ~~Add `_ARRAY_READ_` prefix to `is_internal_variable()`~~ (**FIXED**) | | TM-INF-017 | `set` and `declare -p` leak internal markers | Internal state disclosure (_NAMEREF_, _READONLY_, _UPPER_, _LOWER_) | Filter `is_internal_variable()` names from output | -| TM-INF-018 | `date` builtin returns real host time | Timezone fingerprinting, timing correlation | Configurable time source (fixed or offset) | +| TM-INF-018 | `date` builtin returns real host time | Timezone fingerprinting, timing correlation | `Bash::builder().fixed_epoch(N)` freezes the clock; `Bash::builder().epoch_offset(N)` shifts real-clock by N seconds (mutually exclusive, last call wins). Both implemented via `Date::with_fixed_epoch` / `Date::with_offset_seconds` in `builtins/date.rs`. Default behavior is real clock — embed callers opt in for sandboxing. Regression tests: `tm_inf_018_date::*` in `tests/threat_model_tests.rs`. | **MITIGATED** (opt-in) | | ~~TM-DOS-041~~ | ~~Brace expansion `{N..M}` unbounded range~~ | ~~OOM via `{1..999999999}` allocating billions of strings~~ | Static parser-time check (`MAX_STATIC_BRACE_RANGE = 100_000` in `parser/budget.rs`) rejects oversized literal ranges with `BraceRangeTooLarge`; runtime fallback in `try_expand_range` (`MAX_BRACE_RANGE = 10_000`) treats remaining oversized ranges as literals (**FIXED**) | | ~~TM-DOS-042~~ | ~~Brace expansion combinatorial explosion~~ | ~~OOM via `{1..100}{1..100}{1..100}` = 1M strings~~ | `expand_braces` caps total emitted strings at `MAX_BRACE_EXPANSION_TOTAL = 100_000` and bails out of the recursion when the budget is hit (**FIXED**) | | ~~TM-DOS-043~~ | ~~Arithmetic overflow in `execute_arithmetic_with_side_effects`~~ | ~~Panic (DoS) in debug mode via `((x+=1))` with x=i64::MAX~~ | ~~Use `wrapping_add/sub/mul`~~ (**FIXED**) | @@ -1464,7 +1464,7 @@ This section maps former vulnerability IDs to the new threat ID scheme and track | OverlayFs symlink limit bypass | TM-DOS-045 | `check_write_limits()` + `validate_path()` in `symlink()` | **MITIGATED** | | MountableFs path validation gap | TM-DOS-046 | `validate_path()` in all MountableFs methods | **MITIGATED** | | VFS copy/rename semantic bugs | TM-DOS-047, TM-DOS-048 | Fix limit check in copy(), type check in rename() | **MITIGATED** | -| Date time info leak | TM-INF-018 | Configurable time source | **NEEDED** | +| Date time info leak | TM-INF-018 | `fixed_epoch` + `epoch_offset` builder methods (opt-in) | **MITIGATED** | | Python BashTool.reset() drops limits | TM-PY-028 | `BashTool::reset` rebuilds via `replace_live_bash_with_builder` matching `PyBash::reset` | **MITIGATED** | | YAML parser depth limit | TM-DOS-051 | `depth` parameter on `parse_yaml_block`/`map`/`list` with `MAX_YAML_DEPTH = 100` | **MITIGATED** | | Template engine depth limit | TM-DOS-052 | `depth` parameter on `render_template_inner` with `MAX_TEMPLATE_DEPTH = 100` | **MITIGATED** | diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 011070cb..b488bcef 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -147,10 +147,18 @@ criteria = "safe-to-deploy" version = "1.16.3" criteria = "safe-to-deploy" +[[exemptions.aws-lc-rs]] +version = "1.17.0" +criteria = "safe-to-deploy" + [[exemptions.aws-lc-sys]] version = "0.40.0" criteria = "safe-to-deploy" +[[exemptions.aws-lc-sys]] +version = "0.41.0" +criteria = "safe-to-deploy" + [[exemptions.base16ct]] version = "1.0.0" criteria = "safe-to-deploy" @@ -259,6 +267,10 @@ criteria = "safe-to-deploy" version = "1.2.61" criteria = "safe-to-deploy" +[[exemptions.cc]] +version = "1.2.62" +criteria = "safe-to-deploy" + [[exemptions.cfg-if]] version = "1.0.4" criteria = "safe-to-deploy" @@ -459,6 +471,10 @@ criteria = "safe-to-deploy" version = "0.11.1" criteria = "safe-to-deploy" +[[exemptions.ctor]] +version = "1.0.6" +criteria = "safe-to-deploy" + [[exemptions.ctr]] version = "0.9.2" criteria = "safe-to-deploy" @@ -515,6 +531,10 @@ criteria = "safe-to-deploy" version = "0.11.2" criteria = "safe-to-deploy" +[[exemptions.digest]] +version = "0.11.3" +criteria = "safe-to-deploy" + [[exemptions.displaydoc]] version = "0.2.5" criteria = "safe-to-deploy" @@ -739,6 +759,10 @@ criteria = "safe-to-deploy" version = "0.17.0" criteria = "safe-to-deploy" +[[exemptions.hashbrown]] +version = "0.17.1" +criteria = "safe-to-deploy" + [[exemptions.heapless]] version = "0.7.17" criteria = "safe-to-deploy" @@ -795,6 +819,10 @@ criteria = "safe-to-deploy" version = "0.4.11" criteria = "safe-to-deploy" +[[exemptions.hybrid-array]] +version = "0.4.12" +criteria = "safe-to-deploy" + [[exemptions.hyper]] version = "1.9.0" criteria = "safe-to-deploy" @@ -967,6 +995,10 @@ criteria = "safe-to-deploy" version = "0.3.97" criteria = "safe-to-deploy" +[[exemptions.js-sys]] +version = "0.3.98" +criteria = "safe-to-deploy" + [[exemptions.keccak]] version = "0.2.0" criteria = "safe-to-deploy" @@ -1087,18 +1119,34 @@ criteria = "safe-to-deploy" version = "0.2.2" criteria = "safe-to-deploy" +[[exemptions.module-lattice]] +version = "0.2.3" +criteria = "safe-to-deploy" + [[exemptions.napi]] version = "3.8.6" criteria = "safe-to-deploy" +[[exemptions.napi]] +version = "3.9.0" +criteria = "safe-to-deploy" + [[exemptions.napi-build]] version = "2.3.1" criteria = "safe-to-deploy" +[[exemptions.napi-build]] +version = "2.3.2" +criteria = "safe-to-deploy" + [[exemptions.napi-derive]] version = "3.5.5" criteria = "safe-to-deploy" +[[exemptions.napi-derive]] +version = "3.5.6" +criteria = "safe-to-deploy" + [[exemptions.napi-derive-backend]] version = "5.0.4" criteria = "safe-to-deploy" @@ -1115,6 +1163,10 @@ criteria = "safe-to-deploy" version = "0.31.2" criteria = "safe-to-deploy" +[[exemptions.nix]] +version = "0.31.3" +criteria = "safe-to-deploy" + [[exemptions.nohash-hasher]] version = "0.2.0" criteria = "safe-to-deploy" @@ -1255,6 +1307,10 @@ criteria = "safe-to-deploy" version = "1.0.0" criteria = "safe-to-deploy" +[[exemptions.pack1]] +version = "1.1.0" +criteria = "safe-to-deploy" + [[exemptions.page_size]] version = "0.6.0" criteria = "safe-to-run" @@ -1579,6 +1635,10 @@ criteria = "safe-to-deploy" version = "0.5.0-rc.5" criteria = "safe-to-deploy" +[[exemptions.rfc6979]] +version = "0.5.0" +criteria = "safe-to-deploy" + [[exemptions.ring]] version = "0.17.14" criteria = "safe-to-deploy" @@ -1595,10 +1655,18 @@ criteria = "safe-to-deploy" version = "0.60.2" criteria = "safe-to-deploy" +[[exemptions.russh]] +version = "0.60.3" +criteria = "safe-to-deploy" + [[exemptions.russh-cryptovec]] version = "0.59.0" criteria = "safe-to-deploy" +[[exemptions.russh-cryptovec]] +version = "0.60.3" +criteria = "safe-to-deploy" + [[exemptions.russh-util]] version = "0.52.0" criteria = "safe-to-deploy" @@ -1755,6 +1823,10 @@ criteria = "safe-to-deploy" version = "0.4.2" criteria = "safe-to-deploy" +[[exemptions.serdect]] +version = "0.4.3" +criteria = "safe-to-deploy" + [[exemptions.serial_test]] version = "3.4.0" criteria = "safe-to-run" @@ -1815,6 +1887,10 @@ criteria = "safe-to-deploy" version = "3.0.0-rc.10" criteria = "safe-to-deploy" +[[exemptions.signature]] +version = "3.0.0" +criteria = "safe-to-deploy" + [[exemptions.simd-adler32]] version = "0.3.9" criteria = "safe-to-deploy" @@ -1839,6 +1915,10 @@ criteria = "safe-to-deploy" version = "1.0.2" criteria = "safe-to-deploy" +[[exemptions.siphasher]] +version = "1.0.3" +criteria = "safe-to-deploy" + [[exemptions.slab]] version = "0.4.12" criteria = "safe-to-deploy" @@ -2011,6 +2091,10 @@ criteria = "safe-to-deploy" version = "0.6.8" criteria = "safe-to-deploy" +[[exemptions.tower-http]] +version = "0.6.10" +criteria = "safe-to-deploy" + [[exemptions.tower-layer]] version = "0.3.3" criteria = "safe-to-deploy" @@ -2171,22 +2255,42 @@ criteria = "safe-to-deploy" version = "0.2.120" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen]] +version = "0.2.121" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-futures]] version = "0.4.70" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-futures]] +version = "0.4.71" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-macro]] version = "0.2.120" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-macro]] +version = "0.2.121" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-macro-support]] version = "0.2.120" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-macro-support]] +version = "0.2.121" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-shared]] version = "0.2.120" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-shared]] +version = "0.2.121" +criteria = "safe-to-deploy" + [[exemptions.wasm-encoder]] version = "0.244.0" criteria = "safe-to-deploy" @@ -2207,6 +2311,10 @@ criteria = "safe-to-deploy" version = "0.3.97" criteria = "safe-to-deploy" +[[exemptions.web-sys]] +version = "0.3.98" +criteria = "safe-to-deploy" + [[exemptions.webpki-root-certs]] version = "1.0.7" criteria = "safe-to-deploy" @@ -2315,6 +2423,10 @@ criteria = "safe-to-deploy" version = "0.52.6" criteria = "safe-to-deploy" +[[exemptions.winnow]] +version = "1.0.3" +criteria = "safe-to-deploy" + [[exemptions.wit-bindgen]] version = "0.51.0" criteria = "safe-to-deploy" @@ -2379,6 +2491,10 @@ criteria = "safe-to-deploy" version = "0.1.7" criteria = "safe-to-deploy" +[[exemptions.zerofrom]] +version = "0.1.8" +criteria = "safe-to-deploy" + [[exemptions.zerofrom-derive]] version = "0.1.7" criteria = "safe-to-deploy"