@voxgig/sdkgen 4.6.0 → 4.7.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/dist/cmp/Feature.js +5 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/TestControl.d.ts +4 -0
- package/dist/cmp/TestControl.js +51 -0
- package/dist/cmp/TestControl.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +3 -1
- package/dist/helpers/featureSource.js +105 -0
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -2
- package/dist/sdkgen.js +9 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +52 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +58 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +82 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
- package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
- package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
- package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +2 -1
- package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +2 -1
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
- package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
- package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +2 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -1
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
- package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
- package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
- package/project/.sdk/src/cmp/php/Main_php.ts +2 -1
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -1
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +2 -1
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +2 -1
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +50 -8
- package/project/.sdk/src/cmp/ts/Main_ts.ts +10 -2
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +10 -3
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +11 -3
- package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
- package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
- package/project/.sdk/tm/dart/test/utility.dart +39 -0
- package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/js/test/utility.js +91 -1
- package/project/.sdk/tm/lua/test/runner.lua +40 -0
- package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
- package/project/.sdk/tm/perl/t/runner.pm +30 -0
- package/project/.sdk/tm/php/test/Runner.php +36 -0
- package/project/.sdk/tm/rb/test/runner.rb +25 -0
- package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +8 -2
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +29 -5
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +30 -21
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +15 -13
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +25 -19
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +1 -1
- package/project/sdkgen-package.json +1 -1
- package/src/cmp/Feature.ts +5 -0
- package/src/cmp/TestControl.ts +64 -0
- package/src/helpers/featureSource.ts +130 -0
- package/src/sdkgen.ts +7 -0
- package/src/utility.ts +61 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +0 -1221
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/azuresecrets.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, SekretoError, flatname } from './support'
|
|
7
|
+
import { checkaddr } from './addr'
|
|
8
|
+
import { fetchjson } from './http'
|
|
9
|
+
|
|
10
|
+
export function azuresecretsprovider(options?: {
|
|
11
|
+
vault?: string
|
|
12
|
+
token?: string
|
|
13
|
+
tenant?: string
|
|
14
|
+
clientid?: string
|
|
15
|
+
clientsecret?: string
|
|
16
|
+
loginaddr?: string
|
|
17
|
+
imdsaddr?: string
|
|
18
|
+
apiversion?: string
|
|
19
|
+
}): Provider {
|
|
20
|
+
const opts = options || {}
|
|
21
|
+
const resource = 'https://vault.azure.net'
|
|
22
|
+
|
|
23
|
+
// A configured token is kept forever; logged-in and IMDS tokens carry
|
|
24
|
+
// expires_in and are renewed shortly before they run out.
|
|
25
|
+
let livetoken: string | undefined
|
|
26
|
+
let renewat = Infinity
|
|
27
|
+
|
|
28
|
+
const expiry = (expires: any): number => {
|
|
29
|
+
const seconds = Number(expires)
|
|
30
|
+
return 0 < seconds ? Date.now() + Math.max(seconds - 60, 1) * 1000 : Infinity
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const login = async (): Promise<string> => {
|
|
34
|
+
if (opts.token) {
|
|
35
|
+
return opts.token
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (opts.tenant && opts.clientid && opts.clientsecret) {
|
|
39
|
+
const loginaddr = opts.loginaddr || 'https://login.microsoftonline.com'
|
|
40
|
+
checkaddr(loginaddr)
|
|
41
|
+
|
|
42
|
+
const url = loginaddr.replace(/\/$/, '') + '/' + opts.tenant + '/oauth2/v2.0/token'
|
|
43
|
+
const form =
|
|
44
|
+
'grant_type=client_credentials&client_id=' +
|
|
45
|
+
encodeURIComponent(opts.clientid) +
|
|
46
|
+
'&client_secret=' +
|
|
47
|
+
encodeURIComponent(opts.clientsecret) +
|
|
48
|
+
'&scope=' +
|
|
49
|
+
encodeURIComponent(resource + '/.default')
|
|
50
|
+
|
|
51
|
+
const res = await fetchjson(
|
|
52
|
+
'POST',
|
|
53
|
+
url,
|
|
54
|
+
{ 'content-type': 'application/x-www-form-urlencoded' },
|
|
55
|
+
form,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const got = res.body && res.body.access_token
|
|
59
|
+
if (200 !== res.status || !got) {
|
|
60
|
+
throw new SekretoError('sekreto: azure login failed: ' + res.status)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
renewat = expiry(res.body.expires_in)
|
|
64
|
+
return String(got)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const imds =
|
|
68
|
+
(opts.imdsaddr || 'http://169.254.169.254').replace(/\/$/, '') +
|
|
69
|
+
'/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' +
|
|
70
|
+
encodeURIComponent(resource)
|
|
71
|
+
|
|
72
|
+
const res = await fetchjson('GET', imds, { Metadata: 'true' })
|
|
73
|
+
|
|
74
|
+
const got = res.body && res.body.access_token
|
|
75
|
+
if (200 !== res.status || !got) {
|
|
76
|
+
throw new SekretoError(
|
|
77
|
+
'sekreto: azure: no token, no client credentials, and IMDS did not answer',
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
renewat = expiry(res.body.expires_in)
|
|
82
|
+
return String(got)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
lookup: async (name: string) => {
|
|
87
|
+
const vault = opts.vault || ''
|
|
88
|
+
if ('' === vault) {
|
|
89
|
+
throw new SekretoError('sekreto: azure: no vault')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Only an explicit scheme is a URL; a vault NAMED httpvault must
|
|
93
|
+
// still become https://httpvault.vault.azure.net.
|
|
94
|
+
const vaulturl =
|
|
95
|
+
vault.startsWith('http://') || vault.startsWith('https://')
|
|
96
|
+
? vault
|
|
97
|
+
: 'https://' + vault + '.vault.azure.net'
|
|
98
|
+
checkaddr(vaulturl)
|
|
99
|
+
|
|
100
|
+
if (undefined === livetoken || Date.now() >= renewat) {
|
|
101
|
+
livetoken = await login()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const url =
|
|
105
|
+
vaulturl.replace(/\/$/, '') +
|
|
106
|
+
'/secrets/' +
|
|
107
|
+
flatname(name, '-') +
|
|
108
|
+
'?api-version=' +
|
|
109
|
+
(opts.apiversion || '7.4')
|
|
110
|
+
|
|
111
|
+
const res = await fetchjson('GET', url, { authorization: 'Bearer ' + livetoken })
|
|
112
|
+
|
|
113
|
+
if (404 === res.status) {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (200 !== res.status) {
|
|
118
|
+
throw new SekretoError('sekreto: azure error: ' + res.status + ': ' + url.split('?')[0])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const value = res.body && res.body.value
|
|
122
|
+
return undefined === value || null === value ? undefined : String(value)
|
|
123
|
+
},
|
|
124
|
+
describe: () => 'azuresecrets:' + (opts.vault || ''),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** 1Password, through a Connect server.
|
|
129
|
+
*
|
|
130
|
+
* The item titled `api.token` (titles keep their dots), in the named
|
|
131
|
+
* vault. The value is the field with purpose PASSWORD, or the field
|
|
132
|
+
* labelled `value`. A vault that cannot be found is an error - config
|
|
133
|
+
* names it, so its absence is a broken store, not a missing secret. */
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// Registering at import is what makes this module's presence the only
|
|
137
|
+
// thing that decides whether the kind exists in a build.
|
|
138
|
+
import { register } from './Registry'
|
|
139
|
+
|
|
140
|
+
register({
|
|
141
|
+
name: 'azuresecrets',
|
|
142
|
+
needs: ['fetch'],
|
|
143
|
+
define: (spec: ProviderSpec) => azuresecretsprovider(spec),
|
|
144
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/boru.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, SekretoError, checkname, nodemod } from './support'
|
|
7
|
+
import { checkaddr } from './addr'
|
|
8
|
+
import { fetchjson } from './http'
|
|
9
|
+
|
|
10
|
+
export function boruprovider(options?: {
|
|
11
|
+
command?: string
|
|
12
|
+
namespace?: string
|
|
13
|
+
home?: string
|
|
14
|
+
addr?: string
|
|
15
|
+
token?: string
|
|
16
|
+
mount?: string
|
|
17
|
+
}): Provider {
|
|
18
|
+
const opts = options || {}
|
|
19
|
+
const command = opts.command || 'boru'
|
|
20
|
+
|
|
21
|
+
if (opts.addr) {
|
|
22
|
+
const addr = opts.addr.replace(/\/$/, '')
|
|
23
|
+
const mount = opts.mount || 'secret'
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
lookup: async (name: string) => {
|
|
27
|
+
checkname(name)
|
|
28
|
+
checkaddr(addr)
|
|
29
|
+
|
|
30
|
+
const alias = opts.namespace ? opts.namespace + '/' + name : name
|
|
31
|
+
const url = addr + '/v1/' + mount + '/data/' + alias
|
|
32
|
+
|
|
33
|
+
const res = await fetchjson('GET', url, { 'X-Vault-Token': opts.token || '' })
|
|
34
|
+
|
|
35
|
+
if (404 === res.status) {
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (200 !== res.status) {
|
|
40
|
+
throw new SekretoError('sekreto: boru serve error: ' + res.status + ': ' + url)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = res.body && res.body.data && res.body.data.data
|
|
44
|
+
const value = data ? data['value'] : undefined
|
|
45
|
+
return undefined === value || null === value ? undefined : String(value)
|
|
46
|
+
},
|
|
47
|
+
describe: () => 'boru:' + addr,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
lookup: (name: string) => {
|
|
53
|
+
checkname(name)
|
|
54
|
+
|
|
55
|
+
const alias = opts.namespace ? opts.namespace + ':' + name : name
|
|
56
|
+
const env = opts.home ? { ...process.env, BORU_HOME: opts.home } : process.env
|
|
57
|
+
|
|
58
|
+
const { spawnSync } = nodemod<typeof import('node:child_process')>('node:child_process')
|
|
59
|
+
const run = spawnSync(command, ['vault', 'get', '--reveal', alias], {
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
env,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (run.error) {
|
|
65
|
+
throw new SekretoError('sekreto: cannot run ' + command + ': ' + run.error.message)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (0 === run.status) {
|
|
69
|
+
// boru prints the value and one newline, and nothing else.
|
|
70
|
+
return run.stdout.replace(/\n$/, '')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const why = (run.stderr || '').trim()
|
|
74
|
+
|
|
75
|
+
// "no alias named" is boru saying it does not hold this secret, which
|
|
76
|
+
// is a miss: the chain carries on to the next provider. A locked vault
|
|
77
|
+
// or a wrong passphrase is not a miss - treating it as one would fall
|
|
78
|
+
// through to a weaker store without saying so.
|
|
79
|
+
if (borumiss(why)) {
|
|
80
|
+
return undefined
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new SekretoError('sekreto: boru vault error: ' + (why || 'exit ' + run.status))
|
|
84
|
+
},
|
|
85
|
+
describe: () => 'boru' + (opts.namespace ? ':' + opts.namespace : ''),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Does this boru failure mean "no such secret" rather than "I could not
|
|
90
|
+
* answer"? Matched on boru's own wording for a missing alias. */
|
|
91
|
+
function borumiss(why: string): boolean {
|
|
92
|
+
return /no alias named/.test(why)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The `YYYYMMDDTHHMMSSZ` timestamp SigV4 wants, for now. */
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
// Registering at import is what makes this module's presence the only
|
|
99
|
+
// thing that decides whether the kind exists in a build.
|
|
100
|
+
import { register } from './Registry'
|
|
101
|
+
|
|
102
|
+
register({
|
|
103
|
+
name: 'boru',
|
|
104
|
+
needs: ['fetch'],
|
|
105
|
+
define: (spec: ProviderSpec) => boruprovider({
|
|
106
|
+
command: spec.command,
|
|
107
|
+
namespace: spec.namespace,
|
|
108
|
+
home: spec.home,
|
|
109
|
+
addr: spec.addr,
|
|
110
|
+
token: spec.token,
|
|
111
|
+
mount: spec.mount,
|
|
112
|
+
}),
|
|
113
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/doppler.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, SekretoError, envkey } from './support'
|
|
7
|
+
import { checkaddr } from './addr'
|
|
8
|
+
import { fetchjson } from './http'
|
|
9
|
+
|
|
10
|
+
export function dopplerprovider(options?: {
|
|
11
|
+
token?: string
|
|
12
|
+
project?: string
|
|
13
|
+
config?: string
|
|
14
|
+
addr?: string
|
|
15
|
+
}): Provider {
|
|
16
|
+
const opts = options || {}
|
|
17
|
+
|
|
18
|
+
let values: Record<string, string> | undefined
|
|
19
|
+
|
|
20
|
+
const load = async (): Promise<Record<string, string>> => {
|
|
21
|
+
if (undefined !== values) {
|
|
22
|
+
return values
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const addr = (opts.addr || 'https://api.doppler.com').replace(/\/$/, '')
|
|
26
|
+
checkaddr(addr)
|
|
27
|
+
|
|
28
|
+
let url = addr + '/v3/configs/config/secrets/download?format=json'
|
|
29
|
+
if (opts.project) {
|
|
30
|
+
url += '&project=' + encodeURIComponent(opts.project)
|
|
31
|
+
}
|
|
32
|
+
if (opts.config) {
|
|
33
|
+
url += '&config=' + encodeURIComponent(opts.config)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const res = await fetchjson('GET', url, {
|
|
37
|
+
authorization: 'Bearer ' + (opts.token || ''),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
if (200 !== res.status || !res.body || 'object' !== typeof res.body) {
|
|
41
|
+
throw new SekretoError('sekreto: doppler error: ' + res.status)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
values = {}
|
|
45
|
+
for (const [key, value] of Object.entries(res.body)) {
|
|
46
|
+
if (null !== value && undefined !== value) {
|
|
47
|
+
values[key] = String(value)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return values
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
lookup: async (name: string) => (await load())[envkey(name)],
|
|
56
|
+
describe: () =>
|
|
57
|
+
'doppler' + (opts.project ? ':' + opts.project + '/' + (opts.config || '') : ''),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Infisical.
|
|
62
|
+
*
|
|
63
|
+
* `api.token` reads the secret keyed `API_TOKEN` (Infisical's own
|
|
64
|
+
* convention is environment-style keys) at a secret path in one
|
|
65
|
+
* environment of a project. Auth is a token, or a universal-auth
|
|
66
|
+
* (machine identity) login with clientid/clientsecret. */
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
// Registering at import is what makes this module's presence the only
|
|
70
|
+
// thing that decides whether the kind exists in a build.
|
|
71
|
+
import { register } from './Registry'
|
|
72
|
+
|
|
73
|
+
register({
|
|
74
|
+
name: 'doppler',
|
|
75
|
+
needs: ['fetch'],
|
|
76
|
+
define: (spec: ProviderSpec) => dopplerprovider(spec),
|
|
77
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/dotenv.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, SekretoError, envkey, nodemod, parsedotenv } from './support'
|
|
7
|
+
|
|
8
|
+
export function dotenvprovider(file: string, prefix?: string): Provider {
|
|
9
|
+
let values: Record<string, string> | undefined
|
|
10
|
+
|
|
11
|
+
const load = () => {
|
|
12
|
+
if (undefined === values) {
|
|
13
|
+
try {
|
|
14
|
+
const { readFileSync } = nodemod<typeof import('node:fs')>('node:fs')
|
|
15
|
+
values = parsedotenv(readFileSync(file, 'utf8'))
|
|
16
|
+
} catch (err: any) {
|
|
17
|
+
// An absent file - or an absent directory - means "no secrets
|
|
18
|
+
// here", exactly like fileprovider. Anything else (permission
|
|
19
|
+
// denied, an unreadable mount) is a store that could not answer,
|
|
20
|
+
// and swallowing it would fall through to a weaker store.
|
|
21
|
+
if ('ENOENT' === err.code || 'ENOTDIR' === err.code) {
|
|
22
|
+
values = {}
|
|
23
|
+
} else {
|
|
24
|
+
throw new SekretoError(
|
|
25
|
+
'sekreto: dotenv provider cannot read ' + file + ': ' + err.message,
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return values
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
lookup: (name: string) => load()[envkey(name, prefix)],
|
|
35
|
+
describe: () => 'dotenv:' + file,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Literal values, keyed like environment variables. The spec uses this
|
|
40
|
+
* to test chain behaviour without touching the outside world. */
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// Registering at import is what makes this module's presence the only
|
|
44
|
+
// thing that decides whether the kind exists in a build.
|
|
45
|
+
import { register } from './Registry'
|
|
46
|
+
|
|
47
|
+
register({
|
|
48
|
+
name: 'dotenv',
|
|
49
|
+
needs: ['fs'],
|
|
50
|
+
define: (spec: ProviderSpec) => dotenvprovider(spec.file || '.env', spec.prefix),
|
|
51
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/env.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, envkey } from './support'
|
|
7
|
+
|
|
8
|
+
export function envprovider(prefix?: string, source?: Record<string, any>): Provider {
|
|
9
|
+
const env = source || process.env
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
lookup: (name: string) => {
|
|
13
|
+
const value = env[envkey(name, prefix)]
|
|
14
|
+
return undefined === value || null === value ? undefined : String(value)
|
|
15
|
+
},
|
|
16
|
+
describe: () => 'env' + (prefix ? ':' + prefix : ''),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A `.env` file, read once, keyed exactly like the environment. */
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// Registering at import is what makes this module's presence the only
|
|
24
|
+
// thing that decides whether the kind exists in a build.
|
|
25
|
+
import { register } from './Registry'
|
|
26
|
+
|
|
27
|
+
register({
|
|
28
|
+
name: 'env',
|
|
29
|
+
needs: [],
|
|
30
|
+
define: (spec: ProviderSpec) => envprovider(spec.prefix),
|
|
31
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/file.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ProviderSpec, Provider, SekretoError, envkey, flatname, nodemod,
|
|
8
|
+
} from './support'
|
|
9
|
+
|
|
10
|
+
export function fileprovider(dir: string, prefix?: string): Provider {
|
|
11
|
+
return {
|
|
12
|
+
lookup: (name: string) => {
|
|
13
|
+
const { join } = nodemod<typeof import('node:path')>('node:path')
|
|
14
|
+
const file = join(dir, envkey(name, prefix))
|
|
15
|
+
|
|
16
|
+
let text: string
|
|
17
|
+
try {
|
|
18
|
+
const { readFileSync } = nodemod<typeof import('node:fs')>('node:fs')
|
|
19
|
+
text = readFileSync(file, 'utf8')
|
|
20
|
+
} catch (err: any) {
|
|
21
|
+
// An absent file - or an absent directory - means "no secrets
|
|
22
|
+
// here", exactly like a missing .env. Anything else (permission
|
|
23
|
+
// denied, an unreadable mount) is a store that could not answer.
|
|
24
|
+
if ('ENOENT' === err.code || 'ENOTDIR' === err.code) {
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
throw new SekretoError('sekreto: file provider cannot read ' + file + ': ' + err.message)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return text.replace(/\r?\n$/, '')
|
|
31
|
+
},
|
|
32
|
+
describe: () => 'file:' + dir,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Refuse to send a secret-bearing credential in the clear.
|
|
37
|
+
*
|
|
38
|
+
* A vault API is HTTPS in any real deployment; plaintext is a dev-mode
|
|
39
|
+
* convenience. Sending a token over http to anything but the local
|
|
40
|
+
* machine puts both the token and the secret it fetches on the wire for
|
|
41
|
+
* anyone on the path, so sekreto will not do it. Loopback stays allowed:
|
|
42
|
+
* that is `vault server -dev`, `boru vault serve`, and this repo's own
|
|
43
|
+
* test harness. */
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Registering at import is what makes this module's presence the only
|
|
47
|
+
// thing that decides whether the kind exists in a build.
|
|
48
|
+
import { register } from './Registry'
|
|
49
|
+
|
|
50
|
+
register({
|
|
51
|
+
name: 'file',
|
|
52
|
+
needs: ['fs'],
|
|
53
|
+
define: (spec: ProviderSpec) => fileprovider(spec.dir || '', spec.prefix),
|
|
54
|
+
})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/gcpsecrets.ts)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
/* Copyright (c) 2025 Voxgig Ltd, MIT License */
|
|
5
|
+
|
|
6
|
+
import { ProviderSpec, Provider, SekretoError, flatname, nodemod } from './support'
|
|
7
|
+
import { checkaddr } from './addr'
|
|
8
|
+
import { fetchjson } from './http'
|
|
9
|
+
|
|
10
|
+
export function gcpsecretsprovider(options?: {
|
|
11
|
+
project?: string
|
|
12
|
+
token?: string
|
|
13
|
+
addr?: string
|
|
14
|
+
metadataaddr?: string
|
|
15
|
+
}): Provider {
|
|
16
|
+
const opts = options || {}
|
|
17
|
+
|
|
18
|
+
// A configured token is kept forever; a metadata-server token carries
|
|
19
|
+
// expires_in and is renewed shortly before it runs out.
|
|
20
|
+
let livetoken: string | undefined
|
|
21
|
+
let renewat = Infinity
|
|
22
|
+
|
|
23
|
+
const metadataaddr = (): string => {
|
|
24
|
+
if (opts.metadataaddr) {
|
|
25
|
+
return opts.metadataaddr
|
|
26
|
+
}
|
|
27
|
+
const host = process.env.GCE_METADATA_HOST
|
|
28
|
+
return host ? 'http://' + host : 'http://metadata.google.internal'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const login = async (): Promise<string> => {
|
|
32
|
+
const configured = opts.token || process.env.GOOGLE_OAUTH_ACCESS_TOKEN
|
|
33
|
+
if (configured) {
|
|
34
|
+
return configured
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const url =
|
|
38
|
+
metadataaddr().replace(/\/$/, '') +
|
|
39
|
+
'/computeMetadata/v1/instance/service-accounts/default/token'
|
|
40
|
+
|
|
41
|
+
const res = await fetchjson('GET', url, { 'Metadata-Flavor': 'Google' })
|
|
42
|
+
|
|
43
|
+
const got = res.body && res.body.access_token
|
|
44
|
+
if (200 !== res.status || !got) {
|
|
45
|
+
throw new SekretoError('sekreto: gcp: no token and metadata server did not answer')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const expires = Number(res.body.expires_in)
|
|
49
|
+
renewat = 0 < expires ? Date.now() + Math.max(expires - 60, 1) * 1000 : Infinity
|
|
50
|
+
|
|
51
|
+
return String(got)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
lookup: async (name: string) => {
|
|
56
|
+
const project = opts.project || ''
|
|
57
|
+
if ('' === project) {
|
|
58
|
+
throw new SekretoError('sekreto: gcp: no project')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const addr = opts.addr || 'https://secretmanager.googleapis.com'
|
|
62
|
+
checkaddr(addr)
|
|
63
|
+
|
|
64
|
+
if (undefined === livetoken || Date.now() >= renewat) {
|
|
65
|
+
livetoken = await login()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const url =
|
|
69
|
+
addr.replace(/\/$/, '') +
|
|
70
|
+
'/v1/projects/' +
|
|
71
|
+
project +
|
|
72
|
+
'/secrets/' +
|
|
73
|
+
flatname(name, '_') +
|
|
74
|
+
'/versions/latest:access'
|
|
75
|
+
|
|
76
|
+
const res = await fetchjson('GET', url, { authorization: 'Bearer ' + livetoken })
|
|
77
|
+
|
|
78
|
+
if (404 === res.status) {
|
|
79
|
+
return undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (200 !== res.status) {
|
|
83
|
+
throw new SekretoError('sekreto: gcp error: ' + res.status + ': ' + url)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const data = res.body && res.body.payload && res.body.payload.data
|
|
87
|
+
if ('string' !== typeof data) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return Buffer.from(data, 'base64').toString('utf8')
|
|
92
|
+
},
|
|
93
|
+
describe: () => 'gcpsecrets:' + (opts.project || ''),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Azure Key Vault.
|
|
98
|
+
*
|
|
99
|
+
* `api.token` reads secret `api-token` (dots flattened to `-`; Key
|
|
100
|
+
* Vault names allow nothing else), current version. The token comes
|
|
101
|
+
* from config, then a client-credentials login when tenant/clientid/
|
|
102
|
+
* clientsecret are given, then the IMDS managed-identity endpoint - so
|
|
103
|
+
* on Azure's own platform no credential configuration is needed.
|
|
104
|
+
*
|
|
105
|
+
* As with GCP, the IMDS call is plain http to a link-local host by
|
|
106
|
+
* platform design and carries no credential; the login and vault
|
|
107
|
+
* addresses are `checkaddr`-guarded. */
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
// Registering at import is what makes this module's presence the only
|
|
111
|
+
// thing that decides whether the kind exists in a build.
|
|
112
|
+
import { register } from './Registry'
|
|
113
|
+
|
|
114
|
+
register({
|
|
115
|
+
name: 'gcpsecrets',
|
|
116
|
+
needs: ['fetch'],
|
|
117
|
+
define: (spec: ProviderSpec) => gcpsecretsprovider(spec),
|
|
118
|
+
})
|