@voxgig/sdkgen 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +330 -0
- package/project/.sdk/model/target/c.aon +15 -2
- package/project/.sdk/model/target/cpp.aon +18 -0
- package/project/.sdk/model/target/lua.aon +17 -0
- package/project/.sdk/model/target/ocaml.aon +48 -5
- package/project/.sdk/model/target/zig.aon +30 -4
- package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
- package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
- package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
- package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
- package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
- package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
- package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
- package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
- package/project/.sdk/tm/c/Makefile +58 -9
- package/project/.sdk/tm/c/core/sdk.h +10 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
- package/project/.sdk/tm/c/feature/secrets.c +797 -0
- package/project/.sdk/tm/c/feature/secrets.h +46 -0
- package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
- package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
- package/project/.sdk/tm/cpp/Makefile +48 -4
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
- package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
- package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
- package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
- package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
- package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
- package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
- package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
- package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
- package/project/.sdk/tm/lua/Makefile +15 -4
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
- package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
- package/project/.sdk/tm/ocaml/Makefile +52 -8
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
- package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
- package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
- package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
- package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
- package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
- package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
- package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
- package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
- package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
- package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto sdk-20260908-1556-0 (zig/src/sekreto.zig)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 1267ee2e5f49566bc92695bc9eb3a60ef4924998 [tag: sdk-20260908-1556-0]
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
//! sekreto: one interface for secrets, wherever they live.
|
|
5
|
+
//!
|
|
6
|
+
//! A Sekreto is an ordered chain of providers. `get` asks each in turn and
|
|
7
|
+
//! returns the first hit, so an app can be configured from environment
|
|
8
|
+
//! variables in development and a vault in production without changing a
|
|
9
|
+
//! line of its own code.
|
|
10
|
+
//!
|
|
11
|
+
//! This is the Zig port. The CANONICAL implementation is
|
|
12
|
+
//! ../../typescript/src/Sekreto.ts, and ../../spec/sekreto.json is the
|
|
13
|
+
//! behavioural contract every port runs.
|
|
14
|
+
//!
|
|
15
|
+
//! THE CORE REACHES NO PROVIDER THAT OPENS A SOCKET, SPAWNS A PROCESS OR
|
|
16
|
+
//! SIGNS A REQUEST. The four built-in kinds - env, memory, dotenv, file -
|
|
17
|
+
//! read at most a local file; every other kind is a voxgig/plugin
|
|
18
|
+
//! definition under ../plugins/, and a chain may name one only if the
|
|
19
|
+
//! calling project handed it in through `Options.plugins`. In zig that is
|
|
20
|
+
//! the compiler's rule as much as this library's: a module's imports are
|
|
21
|
+
//! confined to its root's directory, this file is the root of `sekreto`,
|
|
22
|
+
//! and `../plugins/` is outside it. See docs/design/plugin-providers.md.
|
|
23
|
+
//!
|
|
24
|
+
//! Zig has no exceptions, and its error values carry no payload, so a
|
|
25
|
+
//! failure is RETURNED as a message - the shape voxgig/omni's own Zig port
|
|
26
|
+
//! uses. That also keeps the distinction this library cares about most
|
|
27
|
+
//! structural rather than conventional:
|
|
28
|
+
//!
|
|
29
|
+
//! Answer(?[]const u8){ .ok = "value" } a hit
|
|
30
|
+
//! Answer(?[]const u8){ .ok = null } a MISS - the chain carries on
|
|
31
|
+
//! Answer(?[]const u8){ .err = message } a FAILURE - the chain stops
|
|
32
|
+
//!
|
|
33
|
+
//! Memory: every function that can allocate takes the allocator first, and
|
|
34
|
+
//! what it returns (value or message) comes from that allocator. `Sekreto`
|
|
35
|
+
//! owns one allocator for what outlives a lookup (the cache, the redaction
|
|
36
|
+
//! list, the last failure message) and runs each lookup inside an arena it
|
|
37
|
+
//! resets afterwards, so repeated lookups do not grow the heap. What the
|
|
38
|
+
//! plugin host holds - definitions, instances, option maps - lives in
|
|
39
|
+
//! voxgig/plugin's own arena, which is never freed: construction is paid
|
|
40
|
+
//! for once per chain, and a process that builds chains in a loop should
|
|
41
|
+
//! know it.
|
|
42
|
+
|
|
43
|
+
const std = @import("std");
|
|
44
|
+
|
|
45
|
+
/// voxgig/plugin, the one dependency: the host every chain is made of.
|
|
46
|
+
/// Re-exported so a caller can introspect `Sekreto.host` - `list`,
|
|
47
|
+
/// `instance`, `exports` - without wiring the module in a second time.
|
|
48
|
+
pub const plugin = @import("plugin");
|
|
49
|
+
|
|
50
|
+
pub const provider = @import("provider.zig");
|
|
51
|
+
pub const builtins = @import("builtins.zig");
|
|
52
|
+
pub const addr = @import("addr.zig");
|
|
53
|
+
|
|
54
|
+
pub const Provider = provider.Provider;
|
|
55
|
+
pub const ProviderSpec = provider.ProviderSpec;
|
|
56
|
+
pub const KeyValue = provider.KeyValue;
|
|
57
|
+
pub const Auth = provider.Auth;
|
|
58
|
+
pub const Config = provider.Config;
|
|
59
|
+
pub const Definition = provider.Definition;
|
|
60
|
+
pub const providerplugin = provider.providerplugin;
|
|
61
|
+
pub const provide = provider.provide;
|
|
62
|
+
pub const adapt = provider.adapt;
|
|
63
|
+
pub const PROVIDER_EXPORT = provider.PROVIDER_EXPORT;
|
|
64
|
+
pub const ERROR_CODE = provider.ERROR_CODE;
|
|
65
|
+
pub const BUILTINS = builtins.BUILTINS;
|
|
66
|
+
pub const KINDS = builtins.KINDS;
|
|
67
|
+
pub const checkaddr = addr.checkaddr;
|
|
68
|
+
pub const safeaddr = addr.safeaddr;
|
|
69
|
+
|
|
70
|
+
const Allocator = std.mem.Allocator;
|
|
71
|
+
const pv = plugin.value;
|
|
72
|
+
const pt = plugin.types;
|
|
73
|
+
|
|
74
|
+
/// What a fallible call returns: the value, or the message of a failure.
|
|
75
|
+
///
|
|
76
|
+
/// A provider lookup is `Answer(?[]const u8)`, where `.ok = null` is a miss.
|
|
77
|
+
/// A miss and a failure are therefore different cases of different depth,
|
|
78
|
+
/// and no caller can confuse one for the other by accident.
|
|
79
|
+
pub fn Answer(comptime T: type) type {
|
|
80
|
+
return union(enum) {
|
|
81
|
+
ok: T,
|
|
82
|
+
err: []const u8,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// A secret, or a miss.
|
|
87
|
+
pub const Found = Answer(?[]const u8);
|
|
88
|
+
|
|
89
|
+
/// A secret name: dot-separated lowercase segments, e.g. `api.token`.
|
|
90
|
+
pub const Name = []const u8;
|
|
91
|
+
|
|
92
|
+
/// The message of anything sekreto refuses to do: a bad name, a missing
|
|
93
|
+
/// secret, a provider that could not be reached.
|
|
94
|
+
pub fn fail(alloc: Allocator, comptime format: []const u8, args: anytype) Allocator.Error![]const u8 {
|
|
95
|
+
return std.fmt.allocPrint(alloc, format, args);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Is this a well-formed secret name? Dot-separated segments of `[a-z0-9_]`.
|
|
99
|
+
pub fn validname(name: Name) bool {
|
|
100
|
+
if (0 == name.len) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var parts = std.mem.splitScalar(u8, name, '.');
|
|
105
|
+
|
|
106
|
+
while (parts.next()) |part| {
|
|
107
|
+
if (0 == part.len) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
for (part) |ch| {
|
|
111
|
+
const ok = ('a' <= ch and 'z' >= ch) or ('0' <= ch and '9' >= ch) or '_' == ch;
|
|
112
|
+
if (!ok) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// The name itself, or the message that says why it is not one.
|
|
122
|
+
pub fn checkname(alloc: Allocator, name: Name) Allocator.Error!Answer(Name) {
|
|
123
|
+
if (!validname(name)) {
|
|
124
|
+
return .{ .err = try fail(alloc, "sekreto: invalid name: {s}", .{name}) };
|
|
125
|
+
}
|
|
126
|
+
return .{ .ok = name };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// The environment-variable key for a name: `api.token` -> `API_TOKEN`.
|
|
130
|
+
pub fn envkey(alloc: Allocator, name: Name, prefix: []const u8) Allocator.Error!Answer([]const u8) {
|
|
131
|
+
switch (try checkname(alloc, name)) {
|
|
132
|
+
.err => |message| return .{ .err = message },
|
|
133
|
+
.ok => {},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var out: std.ArrayList(u8) = .empty;
|
|
137
|
+
try out.appendSlice(alloc, prefix);
|
|
138
|
+
|
|
139
|
+
for (name) |ch| {
|
|
140
|
+
try out.append(alloc, if ('.' == ch) '_' else std.ascii.toUpper(ch));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return .{ .ok = out.items };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// Where a name lives in a KV vault: `api.token` -> `api` / `token`.
|
|
147
|
+
///
|
|
148
|
+
/// A single-segment name has no path of its own, so it becomes a secret of
|
|
149
|
+
/// that name with the conventional field `value`.
|
|
150
|
+
pub const VaultRef = struct {
|
|
151
|
+
path: []const u8,
|
|
152
|
+
field: []const u8,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
pub fn vaultref(alloc: Allocator, name: Name) Allocator.Error!Answer(VaultRef) {
|
|
156
|
+
switch (try checkname(alloc, name)) {
|
|
157
|
+
.err => |message| return .{ .err = message },
|
|
158
|
+
.ok => {},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const cut = std.mem.lastIndexOfScalar(u8, name, '.') orelse {
|
|
162
|
+
return .{ .ok = .{ .path = name, .field = "value" } };
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
var path: std.ArrayList(u8) = .empty;
|
|
166
|
+
for (name[0..cut]) |ch| {
|
|
167
|
+
try path.append(alloc, if ('.' == ch) '/' else ch);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return .{ .ok = .{ .path = path.items, .field = name[cut + 1 ..] } };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// A name flattened to one segment: `api.token` -> `api_token` (GCP Secret
|
|
174
|
+
/// Manager, `_`) or `api-token` (Azure Key Vault, `-`).
|
|
175
|
+
///
|
|
176
|
+
/// Those stores have no path hierarchy and reject dots in ids, so the dots
|
|
177
|
+
/// become the store's conventional separator. With `-` as the separator,
|
|
178
|
+
/// underscores flatten too: Azure Key Vault's alphabet is letters, digits
|
|
179
|
+
/// and hyphens only, and a valid sekreto name like `with_underscore` must
|
|
180
|
+
/// still be representable there.
|
|
181
|
+
pub fn flatname(alloc: Allocator, name: Name, sep: []const u8) Allocator.Error!Answer([]const u8) {
|
|
182
|
+
switch (try checkname(alloc, name)) {
|
|
183
|
+
.err => |message| return .{ .err = message },
|
|
184
|
+
.ok => {},
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const hyphen = std.mem.eql(u8, "-", sep);
|
|
188
|
+
|
|
189
|
+
var out: std.ArrayList(u8) = .empty;
|
|
190
|
+
for (name) |ch| {
|
|
191
|
+
if ('.' == ch) {
|
|
192
|
+
try out.appendSlice(alloc, sep);
|
|
193
|
+
} else if (hyphen and '_' == ch) {
|
|
194
|
+
try out.append(alloc, '-');
|
|
195
|
+
} else {
|
|
196
|
+
try out.append(alloc, ch);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return .{ .ok = out.items };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/// The AWS SSM Parameter Store name for a name: dots become the path
|
|
204
|
+
/// hierarchy, rooted at `/` (or at a prefix): `db.pass.main` ->
|
|
205
|
+
/// `/db/pass/main`, or `/app/db/pass/main` under prefix `/app`.
|
|
206
|
+
pub fn awsparam(alloc: Allocator, name: Name, prefix: []const u8) Allocator.Error!Answer([]const u8) {
|
|
207
|
+
switch (try checkname(alloc, name)) {
|
|
208
|
+
.err => |message| return .{ .err = message },
|
|
209
|
+
.ok => {},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
var base = prefix;
|
|
213
|
+
var out: std.ArrayList(u8) = .empty;
|
|
214
|
+
|
|
215
|
+
if (0 != base.len and '/' != base[0]) {
|
|
216
|
+
try out.append(alloc, '/');
|
|
217
|
+
}
|
|
218
|
+
if (0 != base.len and '/' == base[base.len - 1]) {
|
|
219
|
+
base = base[0 .. base.len - 1];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try out.appendSlice(alloc, base);
|
|
223
|
+
try out.append(alloc, '/');
|
|
224
|
+
|
|
225
|
+
for (name) |ch| {
|
|
226
|
+
try out.append(alloc, if ('.' == ch) '/' else ch);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return .{ .ok = out.items };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/// A parsed `.env`: raw keys to values, in the order the file gave them.
|
|
233
|
+
///
|
|
234
|
+
/// An ordered list rather than a hash map, because the spec compares the
|
|
235
|
+
/// whole map and a randomised iteration order would make that comparison
|
|
236
|
+
/// depend on the run.
|
|
237
|
+
pub const Dotenv = struct {
|
|
238
|
+
keys: [][]const u8,
|
|
239
|
+
values: [][]const u8,
|
|
240
|
+
|
|
241
|
+
pub fn get(self: Dotenv, key: []const u8) ?[]const u8 {
|
|
242
|
+
for (self.keys, 0..) |candidate, at| {
|
|
243
|
+
if (std.mem.eql(u8, candidate, key)) {
|
|
244
|
+
return self.values[at];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/// Parse `.env` text into raw keys and values.
|
|
252
|
+
///
|
|
253
|
+
/// Deliberately small: `KEY=value`, optional `export`, `#` comments on their
|
|
254
|
+
/// own line, and single- or double-quoted values (double quotes also
|
|
255
|
+
/// unescape `\n`, `\r`, `\t` and `\\`). A line with no `=` is skipped.
|
|
256
|
+
pub fn parsedotenv(alloc: Allocator, text: []const u8) Allocator.Error!Dotenv {
|
|
257
|
+
var keys: std.ArrayList([]const u8) = .empty;
|
|
258
|
+
var values: std.ArrayList([]const u8) = .empty;
|
|
259
|
+
|
|
260
|
+
var lines = std.mem.splitScalar(u8, text, '\n');
|
|
261
|
+
|
|
262
|
+
while (lines.next()) |rawline| {
|
|
263
|
+
var line = rawline;
|
|
264
|
+
if (0 != line.len and '\r' == line[line.len - 1]) {
|
|
265
|
+
line = line[0 .. line.len - 1];
|
|
266
|
+
}
|
|
267
|
+
line = std.mem.trim(u8, line, " \t");
|
|
268
|
+
|
|
269
|
+
if (0 == line.len or '#' == line[0]) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const body = if (std.mem.startsWith(u8, line, "export "))
|
|
274
|
+
std.mem.trim(u8, line[7..], " \t")
|
|
275
|
+
else
|
|
276
|
+
line;
|
|
277
|
+
|
|
278
|
+
const eq = std.mem.indexOfScalar(u8, body, '=') orelse continue;
|
|
279
|
+
if (0 == eq) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const key = std.mem.trim(u8, body[0..eq], " \t");
|
|
284
|
+
var value = std.mem.trim(u8, body[eq + 1 ..], " \t");
|
|
285
|
+
|
|
286
|
+
if (2 <= value.len and '"' == value[0] and '"' == value[value.len - 1]) {
|
|
287
|
+
value = try unescape(alloc, value[1 .. value.len - 1]);
|
|
288
|
+
} else if (2 <= value.len and '\'' == value[0] and '\'' == value[value.len - 1]) {
|
|
289
|
+
value = value[1 .. value.len - 1];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// A repeated key takes the later value, as an assignment does.
|
|
293
|
+
var replaced = false;
|
|
294
|
+
for (keys.items, 0..) |candidate, at| {
|
|
295
|
+
if (std.mem.eql(u8, candidate, key)) {
|
|
296
|
+
values.items[at] = value;
|
|
297
|
+
replaced = true;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!replaced) {
|
|
302
|
+
try keys.append(alloc, key);
|
|
303
|
+
try values.append(alloc, value);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return .{ .keys = keys.items, .values = values.items };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
fn unescape(alloc: Allocator, text: []const u8) Allocator.Error![]const u8 {
|
|
311
|
+
var out: std.ArrayList(u8) = .empty;
|
|
312
|
+
|
|
313
|
+
var index: usize = 0;
|
|
314
|
+
while (index < text.len) : (index += 1) {
|
|
315
|
+
if ('\\' == text[index] and index + 1 < text.len) {
|
|
316
|
+
const next = text[index + 1];
|
|
317
|
+
index += 1;
|
|
318
|
+
switch (next) {
|
|
319
|
+
'n' => try out.append(alloc, '\n'),
|
|
320
|
+
'r' => try out.append(alloc, '\r'),
|
|
321
|
+
't' => try out.append(alloc, '\t'),
|
|
322
|
+
'\\' => try out.append(alloc, '\\'),
|
|
323
|
+
'"' => try out.append(alloc, '"'),
|
|
324
|
+
else => {
|
|
325
|
+
try out.append(alloc, '\\');
|
|
326
|
+
try out.append(alloc, next);
|
|
327
|
+
},
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
try out.append(alloc, text[index]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return out.items;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/// Replace known secret values in text with `[redacted]`.
|
|
338
|
+
///
|
|
339
|
+
/// Only values of four characters or more are replaced: shorter ones are
|
|
340
|
+
/// too likely to appear in ordinary text, and redacting them would make
|
|
341
|
+
/// logs unreadable without making them safer.
|
|
342
|
+
pub fn redact(alloc: Allocator, text: []const u8, values: []const []const u8) Allocator.Error![]const u8 {
|
|
343
|
+
var out: []const u8 = text;
|
|
344
|
+
|
|
345
|
+
// Longest first: a shorter secret that prefixes a longer one used to eat
|
|
346
|
+
// the prefix and leave the rest in the log. Copied first, so the
|
|
347
|
+
// caller's slice is not reordered.
|
|
348
|
+
const usable = try alloc.alloc([]const u8, values.len);
|
|
349
|
+
defer alloc.free(usable);
|
|
350
|
+
|
|
351
|
+
var count: usize = 0;
|
|
352
|
+
for (values) |value| {
|
|
353
|
+
if (4 <= value.len) {
|
|
354
|
+
usable[count] = value;
|
|
355
|
+
count += 1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const longest = struct {
|
|
360
|
+
fn before(_: void, left: []const u8, right: []const u8) bool {
|
|
361
|
+
return left.len > right.len;
|
|
362
|
+
}
|
|
363
|
+
}.before;
|
|
364
|
+
std.mem.sort([]const u8, usable[0..count], {}, longest);
|
|
365
|
+
|
|
366
|
+
for (usable[0..count]) |value| {
|
|
367
|
+
out = try replaceall(alloc, out, value, "[redacted]");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
fn replaceall(
|
|
374
|
+
alloc: Allocator,
|
|
375
|
+
text: []const u8,
|
|
376
|
+
from: []const u8,
|
|
377
|
+
to: []const u8,
|
|
378
|
+
) Allocator.Error![]const u8 {
|
|
379
|
+
if (null == std.mem.indexOf(u8, text, from)) {
|
|
380
|
+
return text;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
var out: std.ArrayList(u8) = .empty;
|
|
384
|
+
var rest = text;
|
|
385
|
+
|
|
386
|
+
while (std.mem.indexOf(u8, rest, from)) |at| {
|
|
387
|
+
try out.appendSlice(alloc, rest[0..at]);
|
|
388
|
+
try out.appendSlice(alloc, to);
|
|
389
|
+
rest = rest[at + from.len ..];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try out.appendSlice(alloc, rest);
|
|
393
|
+
return out.items;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/// Decode standard base64 - GCP payloads and AWS binary secrets.
|
|
397
|
+
pub fn unbase64(alloc: Allocator, text: []const u8) Allocator.Error!?[]const u8 {
|
|
398
|
+
const decoder = std.base64.standard.Decoder;
|
|
399
|
+
|
|
400
|
+
const size = decoder.calcSizeForSlice(text) catch return null;
|
|
401
|
+
const out = try alloc.alloc(u8, size);
|
|
402
|
+
decoder.decode(out, text) catch return null;
|
|
403
|
+
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// One provider in the chain, under the store name it answers to, and the
|
|
408
|
+
/// ref of the plugin instance that built it.
|
|
409
|
+
const Entry = struct {
|
|
410
|
+
store: []const u8,
|
|
411
|
+
ref: []const u8,
|
|
412
|
+
provider: Provider,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/// One resolved value, under the store the CALLER named ("" for a
|
|
416
|
+
/// transparent `get`). Kept as a list rather than a map so that the store
|
|
417
|
+
/// stays attached, and so redaction order is stable.
|
|
418
|
+
///
|
|
419
|
+
/// `store` and `name` are copies: they arrive from the caller and must not
|
|
420
|
+
/// outlive it. `value` is not - it points at the copy `seen` already owns,
|
|
421
|
+
/// so a cached secret is stored once and freed once.
|
|
422
|
+
const Cached = struct {
|
|
423
|
+
store: []const u8,
|
|
424
|
+
name: []const u8,
|
|
425
|
+
value: []const u8,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/// What `Sekreto.init` takes.
|
|
429
|
+
pub const Options = struct {
|
|
430
|
+
/// The chain, in resolution order: each entry names a kind to build -
|
|
431
|
+
/// a built-in, or a plugin passed in `plugins`.
|
|
432
|
+
providers: []const ProviderSpec = &.{},
|
|
433
|
+
/// The provider kinds beyond the built-ins that `providers` may name,
|
|
434
|
+
/// as voxgig/plugin definitions. Static and explicit: the calling
|
|
435
|
+
/// project imports the plugins it needs and passes them here, and a
|
|
436
|
+
/// kind it did not pass is unknown to this Sekreto.
|
|
437
|
+
plugins: []const Definition = &.{},
|
|
438
|
+
/// Cache resolved values (default: true).
|
|
439
|
+
cache: bool = true,
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/// A slice with nothing in it, for a closed chain: `[]Entry` cannot be
|
|
443
|
+
/// made from an empty literal without discarding const.
|
|
444
|
+
var noentries: [0]Entry = .{};
|
|
445
|
+
|
|
446
|
+
/// The message for a kind the catalog does not hold.
|
|
447
|
+
///
|
|
448
|
+
/// A kind sekreto has never heard of is a typo; a kind that exists as a
|
|
449
|
+
/// plugin but was not passed in is the split working as designed and
|
|
450
|
+
/// telling you what to pass. Collapsing the two was the first thing that
|
|
451
|
+
/// made the split confusing to use.
|
|
452
|
+
fn unknownkind(alloc: Allocator, kind: []const u8, catalog: *plugin.catalog.Catalog) Allocator.Error![]const u8 {
|
|
453
|
+
var out: std.ArrayList(u8) = .empty;
|
|
454
|
+
errdefer out.deinit(alloc);
|
|
455
|
+
|
|
456
|
+
try out.appendSlice(alloc, "sekreto: unknown provider kind: ");
|
|
457
|
+
try out.appendSlice(alloc, kind);
|
|
458
|
+
try out.appendSlice(alloc, " (available: ");
|
|
459
|
+
for (pv.items(catalog.names()), 0..) |name, at| {
|
|
460
|
+
if (0 < at) {
|
|
461
|
+
try out.appendSlice(alloc, ", ");
|
|
462
|
+
}
|
|
463
|
+
try out.appendSlice(alloc, pv.asStr(name));
|
|
464
|
+
}
|
|
465
|
+
try out.appendSlice(alloc, ")");
|
|
466
|
+
|
|
467
|
+
for (KINDS.plugin) |known| {
|
|
468
|
+
if (std.mem.eql(u8, known, kind)) {
|
|
469
|
+
try out.appendSlice(alloc, " - ");
|
|
470
|
+
try out.appendSlice(alloc, kind);
|
|
471
|
+
try out.appendSlice(alloc, " is a sekreto plugin, not built in: pass it in the plugins option");
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return out.toOwnedSlice(alloc);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/// The parked plugin error, as a message the caller owns. A sekreto
|
|
480
|
+
/// failure that crossed the plugin boundary comes back out as itself,
|
|
481
|
+
/// byte for byte; anything else is the host's report, as the host words
|
|
482
|
+
/// it, and not sekreto's to rewrite.
|
|
483
|
+
fn unwrap(alloc: Allocator) Allocator.Error![]const u8 {
|
|
484
|
+
const err = pt.take();
|
|
485
|
+
const cause = pv.get(err.details, "cause");
|
|
486
|
+
|
|
487
|
+
if (std.mem.eql(u8, ERROR_CODE, err.code) and pv.isStr(cause)) {
|
|
488
|
+
return alloc.dupe(u8, pv.asStr(cause));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return alloc.dupe(u8, err.message);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/// One definition into the catalog: a copy the host can keep, since the
|
|
495
|
+
/// catalog holds pointers and the caller's slice is its own.
|
|
496
|
+
fn register(alloc: Allocator, catalog: *plugin.catalog.Catalog, definition: Definition) Allocator.Error!?[]const u8 {
|
|
497
|
+
const copy = pv.arena().create(Definition) catch return error.OutOfMemory;
|
|
498
|
+
copy.* = definition;
|
|
499
|
+
catalog.add(copy) catch return try unwrap(alloc);
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/// One chain entry, as a plugin instance.
|
|
504
|
+
///
|
|
505
|
+
/// The instance is `kind` for a store named after its kind and
|
|
506
|
+
/// `kind$store` otherwise - `hashicorp$prod` - so `host.list()` reads like
|
|
507
|
+
/// the chain. A store name that is already taken gets a numbered tag from
|
|
508
|
+
/// the host instead, because two providers MAY share a store name (a
|
|
509
|
+
/// directed read walks both) and an instance ref may not.
|
|
510
|
+
fn declare(
|
|
511
|
+
alloc: Allocator,
|
|
512
|
+
host: *plugin.host.Host,
|
|
513
|
+
catalog: *plugin.catalog.Catalog,
|
|
514
|
+
b: *provider.Building,
|
|
515
|
+
spec: ProviderSpec,
|
|
516
|
+
) Allocator.Error!Answer(Entry) {
|
|
517
|
+
const kind = spec.kind;
|
|
518
|
+
|
|
519
|
+
if (!catalog.has(kind)) {
|
|
520
|
+
return .{ .err = try unknownkind(alloc, kind, catalog) };
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const store = if (0 != spec.name.len) spec.name else kind;
|
|
524
|
+
|
|
525
|
+
if (!plugin.ref.checktag(pv.vstr(store))) {
|
|
526
|
+
return .{ .err = try fail(alloc, "sekreto: invalid store name: {s}", .{store}) };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
var ref: []const u8 = kind;
|
|
530
|
+
if (!std.mem.eql(u8, store, kind)) {
|
|
531
|
+
ref = plugin.ref.formatref(pv.vstr(kind), pv.vstr(store)) catch return .{ .err = try unwrap(alloc) };
|
|
532
|
+
}
|
|
533
|
+
const taken = plugin.host.instance(host, ref) catch return .{ .err = try unwrap(alloc) };
|
|
534
|
+
if (null != taken) {
|
|
535
|
+
ref = plugin.host.autotag(host, kind) catch return .{ .err = try unwrap(alloc) };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// `load` runs the definition's `define`, which builds the provider
|
|
539
|
+
// from the spec; `activate` takes the instance live. Nothing is
|
|
540
|
+
// contacted by either: a provider opens nothing until its first
|
|
541
|
+
// lookup.
|
|
542
|
+
_ = plugin.host.load(host, ref, .{ .options = provider.optionsof(spec) }) catch {
|
|
543
|
+
if (b.oom) {
|
|
544
|
+
_ = pt.take();
|
|
545
|
+
return error.OutOfMemory;
|
|
546
|
+
}
|
|
547
|
+
return .{ .err = try unwrap(alloc) };
|
|
548
|
+
};
|
|
549
|
+
_ = plugin.host.activate(host, ref) catch return .{ .err = try unwrap(alloc) };
|
|
550
|
+
|
|
551
|
+
const key = try std.fmt.allocPrint(alloc, "{s}/{s}", .{ ref, PROVIDER_EXPORT });
|
|
552
|
+
defer alloc.free(key);
|
|
553
|
+
|
|
554
|
+
const exported = plugin.host.exports(host, key) catch return .{ .err = try unwrap(alloc) };
|
|
555
|
+
if (!pv.isNum(exported)) {
|
|
556
|
+
return .{ .err = try fail(alloc, "sekreto: plugin {s} exported no provider", .{kind}) };
|
|
557
|
+
}
|
|
558
|
+
const index: usize = @intFromFloat(pv.asNum(exported));
|
|
559
|
+
|
|
560
|
+
return .{ .ok = .{
|
|
561
|
+
.store = try alloc.dupe(u8, store),
|
|
562
|
+
.ref = try alloc.dupe(u8, ref),
|
|
563
|
+
.provider = b.made.items[index],
|
|
564
|
+
} };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/// Undo a chain that could not be finished: the host closed, every
|
|
568
|
+
/// provider built so far released, every store name freed.
|
|
569
|
+
fn teardown(alloc: Allocator, host: *plugin.host.Host, b: *provider.Building, entries: *std.ArrayList(Entry)) void {
|
|
570
|
+
plugin.host.close(host) catch {
|
|
571
|
+
_ = pt.take();
|
|
572
|
+
};
|
|
573
|
+
for (b.made.items) |made| {
|
|
574
|
+
made.deinit(alloc);
|
|
575
|
+
}
|
|
576
|
+
for (entries.items) |entry| {
|
|
577
|
+
alloc.free(entry.store);
|
|
578
|
+
alloc.free(entry.ref);
|
|
579
|
+
}
|
|
580
|
+
entries.deinit(alloc);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/// The secrets facade: a chain of providers plus a cache.
|
|
584
|
+
///
|
|
585
|
+
/// Two ways to read. `get` is transparent - it walks the chain and takes the
|
|
586
|
+
/// first hit, and the caller never learns which store answered. `getfrom` is
|
|
587
|
+
/// directed - it names the store, and only that store is asked. Use the
|
|
588
|
+
/// first for ordinary configuration, the second when *which* store holds a
|
|
589
|
+
/// secret is part of what you mean.
|
|
590
|
+
pub const Sekreto = struct {
|
|
591
|
+
alloc: Allocator,
|
|
592
|
+
/// The voxgig/plugin host every chain entry is an instance of. Read it
|
|
593
|
+
/// for introspection - `plugin.host.list(secrets.host)` names each
|
|
594
|
+
/// store's ref and status - and nothing on it advances the chain.
|
|
595
|
+
host: *plugin.host.Host,
|
|
596
|
+
/// The definitions this Sekreto can build: the built-ins plus what
|
|
597
|
+
/// `Options.plugins` handed in.
|
|
598
|
+
catalog: *plugin.catalog.Catalog,
|
|
599
|
+
entries: []Entry,
|
|
600
|
+
docache: bool,
|
|
601
|
+
cache: std.ArrayList(Cached),
|
|
602
|
+
// Every value ever resolved, for redact(). Kept independently of the
|
|
603
|
+
// read cache so that redaction still works when caching is off -
|
|
604
|
+
// otherwise `cache: false` would silently disable redact() and leak
|
|
605
|
+
// secrets to logs. It also owns every value string the cache points at.
|
|
606
|
+
seen: std.ArrayList([]const u8),
|
|
607
|
+
// Scratch for one lookup: URLs, request bodies, parsed responses. Reset
|
|
608
|
+
// after every lookup, so a long-running process that reads the same
|
|
609
|
+
// secrets over and over does not grow.
|
|
610
|
+
scratch: std.heap.ArenaAllocator,
|
|
611
|
+
// The most recent failure message, owned here so that a caller need not
|
|
612
|
+
// free it and a retry loop cannot accumulate them.
|
|
613
|
+
lasterr: ?[]const u8,
|
|
614
|
+
|
|
615
|
+
/// Build a chain: a catalog of the built-in kinds plus the plugins, a
|
|
616
|
+
/// voxgig/plugin host, and one instance of the right kind per chain
|
|
617
|
+
/// entry. It fails on a kind the catalog does not hold, a store name
|
|
618
|
+
/// that is not a valid tag, or a provider that refuses its own
|
|
619
|
+
/// configuration.
|
|
620
|
+
///
|
|
621
|
+
/// Heap-allocated, not returned by value: the scratch arena hands out an
|
|
622
|
+
/// allocator that points at its own address, so a Sekreto that moved
|
|
623
|
+
/// after construction would hand every lookup a dangling one.
|
|
624
|
+
///
|
|
625
|
+
/// A failure here hands back a message the CALLER owns - there is no
|
|
626
|
+
/// Sekreto yet to own it. Once there is one, every message a lookup
|
|
627
|
+
/// returns belongs to the Sekreto and is freed by `deinit`.
|
|
628
|
+
pub fn init(alloc: Allocator, config: Config, options: Options) Allocator.Error!Answer(*Sekreto) {
|
|
629
|
+
// Built-ins first, then the plugins, into one catalog: a plugin
|
|
630
|
+
// that names a built-in kind replaces it, which is how a host
|
|
631
|
+
// substitutes an implementation and never an accident, because the
|
|
632
|
+
// four names are documented.
|
|
633
|
+
const catalog = plugin.catalog.makecatalog();
|
|
634
|
+
for (BUILTINS) |definition| {
|
|
635
|
+
if (try register(alloc, catalog, definition)) |message| {
|
|
636
|
+
return .{ .err = message };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
for (options.plugins) |definition| {
|
|
640
|
+
if (try register(alloc, catalog, definition)) |message| {
|
|
641
|
+
return .{ .err = message };
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const host = plugin.host.makehost(.{ .catalog = catalog });
|
|
646
|
+
|
|
647
|
+
var b = provider.Building{ .alloc = alloc, .config = config };
|
|
648
|
+
provider.building = &b;
|
|
649
|
+
defer provider.building = null;
|
|
650
|
+
defer b.made.deinit(alloc);
|
|
651
|
+
|
|
652
|
+
var entries: std.ArrayList(Entry) = .empty;
|
|
653
|
+
// A chain that cannot be built is not left half-built. `errdefer`
|
|
654
|
+
// covers running out of memory; the `.err` arm covers a spec this
|
|
655
|
+
// library refuses, which is a returned value rather than a Zig
|
|
656
|
+
// error and so is not an errdefer's business.
|
|
657
|
+
errdefer teardown(alloc, host, &b, &entries);
|
|
658
|
+
|
|
659
|
+
for (options.providers) |spec| {
|
|
660
|
+
switch (try declare(alloc, host, catalog, &b, spec)) {
|
|
661
|
+
.err => |message| {
|
|
662
|
+
teardown(alloc, host, &b, &entries);
|
|
663
|
+
return .{ .err = message };
|
|
664
|
+
},
|
|
665
|
+
.ok => |entry| try entries.append(alloc, entry),
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const self = try alloc.create(Sekreto);
|
|
670
|
+
errdefer alloc.destroy(self);
|
|
671
|
+
|
|
672
|
+
self.* = .{
|
|
673
|
+
.alloc = alloc,
|
|
674
|
+
.host = host,
|
|
675
|
+
.catalog = catalog,
|
|
676
|
+
.entries = try entries.toOwnedSlice(alloc),
|
|
677
|
+
.docache = options.cache,
|
|
678
|
+
.cache = .empty,
|
|
679
|
+
.seen = .empty,
|
|
680
|
+
.scratch = std.heap.ArenaAllocator.init(alloc),
|
|
681
|
+
.lasterr = null,
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
return .{ .ok = self };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/// Tear the chain down: every plugin instance is deactivated and
|
|
688
|
+
/// unloaded, in reverse, releasing whatever a provider acquired at
|
|
689
|
+
/// activation, and every provider is freed. Afterwards there is
|
|
690
|
+
/// nothing to read from - `get` reports every secret unknown - and the
|
|
691
|
+
/// cache is dropped, though `redactText` still knows every value that
|
|
692
|
+
/// was ever resolved. Idempotent.
|
|
693
|
+
pub fn close(self: *Sekreto) void {
|
|
694
|
+
plugin.host.close(self.host) catch {
|
|
695
|
+
_ = pt.take();
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
for (self.entries) |entry| {
|
|
699
|
+
entry.provider.deinit(self.alloc);
|
|
700
|
+
self.alloc.free(entry.store);
|
|
701
|
+
self.alloc.free(entry.ref);
|
|
702
|
+
}
|
|
703
|
+
self.alloc.free(self.entries);
|
|
704
|
+
self.entries = &noentries;
|
|
705
|
+
|
|
706
|
+
self.refresh();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
pub fn deinit(self: *Sekreto) void {
|
|
710
|
+
self.close();
|
|
711
|
+
|
|
712
|
+
for (self.seen.items) |value| {
|
|
713
|
+
self.alloc.free(value);
|
|
714
|
+
}
|
|
715
|
+
self.seen.deinit(self.alloc);
|
|
716
|
+
self.cache.deinit(self.alloc);
|
|
717
|
+
|
|
718
|
+
if (self.lasterr) |message| {
|
|
719
|
+
self.alloc.free(message);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
self.scratch.deinit();
|
|
723
|
+
self.alloc.destroy(self);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/// The secret, or a failure if no provider has it.
|
|
727
|
+
pub fn get(self: *Sekreto, name: Name) Allocator.Error!Answer([]const u8) {
|
|
728
|
+
switch (try self.trysecret(name)) {
|
|
729
|
+
.err => |message| return .{ .err = message },
|
|
730
|
+
.ok => |found| {
|
|
731
|
+
const value = found orelse
|
|
732
|
+
return .{ .err = try self.keep("sekreto: unknown secret: {s}", .{name}) };
|
|
733
|
+
return .{ .ok = value };
|
|
734
|
+
},
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/// The secret, or null if no provider has it.
|
|
739
|
+
///
|
|
740
|
+
/// Named `trysecret` because `try` is a Zig keyword; every port names
|
|
741
|
+
/// this the closest thing its language allows.
|
|
742
|
+
pub fn trysecret(self: *Sekreto, name: Name) Allocator.Error!Found {
|
|
743
|
+
return self.resolve("", name, self.entries);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/// The secret from one named store, or a failure if that store does not
|
|
747
|
+
/// have it.
|
|
748
|
+
pub fn getfrom(self: *Sekreto, store: []const u8, name: Name) Allocator.Error!Answer([]const u8) {
|
|
749
|
+
switch (try self.tryfrom(store, name)) {
|
|
750
|
+
.err => |message| return .{ .err = message },
|
|
751
|
+
.ok => |found| {
|
|
752
|
+
const value = found orelse return .{
|
|
753
|
+
.err = try self.keep("sekreto: unknown secret: {s}:{s}", .{ store, name }),
|
|
754
|
+
};
|
|
755
|
+
return .{ .ok = value };
|
|
756
|
+
},
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/// The secret from one named store, or null if that store does not have
|
|
761
|
+
/// it.
|
|
762
|
+
///
|
|
763
|
+
/// Naming a store that is not in the chain is an error, not a miss:
|
|
764
|
+
/// `try` already means "this store may not have it", so it cannot also
|
|
765
|
+
/// mean "this store may not exist" without hiding a typo.
|
|
766
|
+
pub fn tryfrom(self: *Sekreto, store: []const u8, name: Name) Allocator.Error!Found {
|
|
767
|
+
var matching: std.ArrayList(Entry) = .empty;
|
|
768
|
+
defer matching.deinit(self.alloc);
|
|
769
|
+
|
|
770
|
+
for (self.entries) |entry| {
|
|
771
|
+
if (std.mem.eql(u8, entry.store, store)) {
|
|
772
|
+
try matching.append(self.alloc, entry);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (0 == matching.items.len) {
|
|
777
|
+
return .{ .err = try self.keep("sekreto: unknown store: {s}", .{store}) };
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return self.resolve(store, name, matching.items);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
fn resolve(self: *Sekreto, store: []const u8, name: Name, entries: []const Entry) Allocator.Error!Found {
|
|
784
|
+
const work = self.scratch.allocator();
|
|
785
|
+
// The scratch arena holds only what this lookup needs; anything that
|
|
786
|
+
// outlives it is copied into self.alloc before the reset.
|
|
787
|
+
defer _ = self.scratch.reset(.retain_capacity);
|
|
788
|
+
|
|
789
|
+
switch (try checkname(work, name)) {
|
|
790
|
+
.err => |message| return .{ .err = try self.keepraw(message) },
|
|
791
|
+
.ok => {},
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (self.docache) {
|
|
795
|
+
for (self.cache.items) |entry| {
|
|
796
|
+
if (std.mem.eql(u8, entry.store, store) and std.mem.eql(u8, entry.name, name)) {
|
|
797
|
+
return .{ .ok = entry.value };
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
for (entries) |entry| {
|
|
803
|
+
switch (try entry.provider.lookup(work, name)) {
|
|
804
|
+
.err => |message| return .{ .err = try self.keepraw(message) },
|
|
805
|
+
.ok => |found| {
|
|
806
|
+
const value = found orelse continue;
|
|
807
|
+
|
|
808
|
+
// `seen` owns every resolved value; the cache points at
|
|
809
|
+
// the same bytes, so nothing is stored twice or freed
|
|
810
|
+
// twice.
|
|
811
|
+
const kept = try self.alloc.dupe(u8, value);
|
|
812
|
+
try self.seen.append(self.alloc, kept);
|
|
813
|
+
|
|
814
|
+
if (self.docache) {
|
|
815
|
+
// Keyed by the store the CALLER named - the empty
|
|
816
|
+
// string for a transparent `get` - not by whichever
|
|
817
|
+
// provider answered. A transparent read and a
|
|
818
|
+
// directed one are different questions, and a cache
|
|
819
|
+
// keyed by the answering provider would never
|
|
820
|
+
// satisfy the first of them.
|
|
821
|
+
try self.cache.append(self.alloc, .{
|
|
822
|
+
.store = try self.alloc.dupe(u8, store),
|
|
823
|
+
.name = try self.alloc.dupe(u8, name),
|
|
824
|
+
.value = kept,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return .{ .ok = kept };
|
|
829
|
+
},
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return .{ .ok = null };
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/// Does any provider have this secret?
|
|
837
|
+
pub fn has(self: *Sekreto, name: Name) Allocator.Error!Answer(bool) {
|
|
838
|
+
switch (try self.trysecret(name)) {
|
|
839
|
+
.err => |message| return .{ .err = message },
|
|
840
|
+
.ok => |found| return .{ .ok = null != found },
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/// Does this named store have this secret?
|
|
845
|
+
pub fn hasin(self: *Sekreto, store: []const u8, name: Name) Allocator.Error!Answer(bool) {
|
|
846
|
+
switch (try self.tryfrom(store, name)) {
|
|
847
|
+
.err => |message| return .{ .err = message },
|
|
848
|
+
.ok => |found| return .{ .ok = null != found },
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/// Every named secret at once. Missing ones are a failure.
|
|
853
|
+
///
|
|
854
|
+
/// The values are the Sekreto's own; the returned list comes from
|
|
855
|
+
/// `alloc`.
|
|
856
|
+
pub fn all(
|
|
857
|
+
self: *Sekreto,
|
|
858
|
+
alloc: Allocator,
|
|
859
|
+
names: []const Name,
|
|
860
|
+
) Allocator.Error!Answer([]const []const u8) {
|
|
861
|
+
var out: std.ArrayList([]const u8) = .empty;
|
|
862
|
+
|
|
863
|
+
for (names) |name| {
|
|
864
|
+
switch (try self.get(name)) {
|
|
865
|
+
.err => |message| return .{ .err = message },
|
|
866
|
+
.ok => |value| try out.append(alloc, value),
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return .{ .ok = out.items };
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/// A description of each provider, in resolution order.
|
|
874
|
+
pub fn sources(self: *const Sekreto, alloc: Allocator) Allocator.Error![]const []const u8 {
|
|
875
|
+
var out: std.ArrayList([]const u8) = .empty;
|
|
876
|
+
|
|
877
|
+
for (self.entries) |entry| {
|
|
878
|
+
try out.append(alloc, try entry.provider.describe(alloc));
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return out.items;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/// The name of each store that can be named by `getfrom`, in resolution
|
|
885
|
+
/// order and without repeats.
|
|
886
|
+
pub fn stores(self: *const Sekreto, alloc: Allocator) Allocator.Error![]const []const u8 {
|
|
887
|
+
var out: std.ArrayList([]const u8) = .empty;
|
|
888
|
+
|
|
889
|
+
for (self.entries) |entry| {
|
|
890
|
+
var seen = false;
|
|
891
|
+
for (out.items) |candidate| {
|
|
892
|
+
if (std.mem.eql(u8, candidate, entry.store)) {
|
|
893
|
+
seen = true;
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (!seen) {
|
|
898
|
+
try out.append(alloc, entry.store);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return out.items;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/// Replace every value this Sekreto has resolved with `[redacted]`.
|
|
906
|
+
///
|
|
907
|
+
/// Works whether or not caching is enabled: the redaction list is kept
|
|
908
|
+
/// independently of the read cache.
|
|
909
|
+
pub fn redactText(
|
|
910
|
+
self: *const Sekreto,
|
|
911
|
+
alloc: Allocator,
|
|
912
|
+
text: []const u8,
|
|
913
|
+
) Allocator.Error![]const u8 {
|
|
914
|
+
return redact(alloc, text, self.seen.items);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/// Drop cached values, so the next `get` asks the providers again.
|
|
918
|
+
pub fn refresh(self: *Sekreto) void {
|
|
919
|
+
for (self.cache.items) |entry| {
|
|
920
|
+
self.alloc.free(entry.store);
|
|
921
|
+
self.alloc.free(entry.name);
|
|
922
|
+
}
|
|
923
|
+
self.cache.clearRetainingCapacity();
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// One failure message, owned by the Sekreto so that a caller never has
|
|
927
|
+
// to free it and a retry loop cannot pile them up.
|
|
928
|
+
fn keep(self: *Sekreto, comptime format: []const u8, args: anytype) Allocator.Error![]const u8 {
|
|
929
|
+
if (self.lasterr) |old| {
|
|
930
|
+
self.alloc.free(old);
|
|
931
|
+
self.lasterr = null;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const message = try std.fmt.allocPrint(self.alloc, format, args);
|
|
935
|
+
self.lasterr = message;
|
|
936
|
+
return message;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// The same, for a message built in the scratch arena: it must be copied
|
|
940
|
+
// out before the arena is reset.
|
|
941
|
+
fn keepraw(self: *Sekreto, message: []const u8) Allocator.Error![]const u8 {
|
|
942
|
+
return self.keep("{s}", .{message});
|
|
943
|
+
}
|
|
944
|
+
};
|