@clef-sh/core 0.1.11-beta.74 → 0.1.12-beta.85
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/dist/artifact/output.d.ts +18 -0
- package/dist/artifact/output.d.ts.map +1 -0
- package/dist/artifact/packer.d.ts.map +1 -1
- package/dist/artifact/types.d.ts +9 -3
- package/dist/artifact/types.d.ts.map +1 -1
- package/dist/cloud/credentials.d.ts.map +1 -1
- package/dist/cloud/device-flow.d.ts +12 -2
- package/dist/cloud/device-flow.d.ts.map +1 -1
- package/dist/cloud/index.d.ts +1 -1
- package/dist/cloud/index.d.ts.map +1 -1
- package/dist/cloud/pack-client.d.ts +1 -1
- package/dist/cloud/pack-client.d.ts.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -39
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +110 -39
- package/dist/index.mjs.map +4 -4
- package/dist/manifest/parser.d.ts.map +1 -1
- package/dist/migration/backend.d.ts +9 -2
- package/dist/migration/backend.d.ts.map +1 -1
- package/dist/report/cloud-client.d.ts +1 -1
- package/dist/report/sanitizer.d.ts +1 -1
- package/dist/report/transformer.d.ts +1 -1
- package/dist/sops/client.d.ts +8 -0
- package/dist/sops/client.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -535,6 +535,12 @@ var ManifestParser = class {
|
|
|
535
535
|
);
|
|
536
536
|
}
|
|
537
537
|
const siName = siObj.name;
|
|
538
|
+
if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(siName)) {
|
|
539
|
+
throw new ManifestValidationError(
|
|
540
|
+
`Service identity '${siName}' has an invalid name. Names must be lowercase alphanumeric with hyphens, must not start or end with a hyphen, and max 63 characters (e.g. 'api-gateway', 'auth-service').`,
|
|
541
|
+
"service_identities"
|
|
542
|
+
);
|
|
543
|
+
}
|
|
538
544
|
if (siObj.description != null && typeof siObj.description !== "string") {
|
|
539
545
|
throw new ManifestValidationError(
|
|
540
546
|
`Service identity '${siName}' has a non-string 'description'.`,
|
|
@@ -686,7 +692,7 @@ var ManifestParser = class {
|
|
|
686
692
|
"cloud"
|
|
687
693
|
);
|
|
688
694
|
}
|
|
689
|
-
if (!/^clef:[a-
|
|
695
|
+
if (!/^clef:[a-zA-Z0-9_]+\/[a-zA-Z0-9_-]+$/.test(cloudObj.keyId)) {
|
|
690
696
|
throw new ManifestValidationError(
|
|
691
697
|
`Field 'cloud.keyId' has invalid format '${cloudObj.keyId}'. Must match: clef:<integrationId>/<keyAlias>`,
|
|
692
698
|
"cloud"
|
|
@@ -2098,6 +2104,13 @@ function openWindowsInputPipe(content) {
|
|
|
2098
2104
|
});
|
|
2099
2105
|
});
|
|
2100
2106
|
}
|
|
2107
|
+
function cloudKeyToArn(keyId) {
|
|
2108
|
+
const body = keyId.replace(/^clef:/, "");
|
|
2109
|
+
const sep = body.indexOf("/");
|
|
2110
|
+
const integration = sep >= 0 ? body.slice(0, sep) : body;
|
|
2111
|
+
const env = sep >= 0 ? body.slice(sep + 1) : "default";
|
|
2112
|
+
return `arn:aws:kms:us-east-1:000000000000:alias/clef/${integration}/${env}`;
|
|
2113
|
+
}
|
|
2101
2114
|
var SopsClient = class {
|
|
2102
2115
|
/**
|
|
2103
2116
|
* @param runner - Subprocess runner used to invoke the `sops` binary.
|
|
@@ -2143,7 +2156,7 @@ var SopsClient = class {
|
|
|
2143
2156
|
const env = this.buildSopsEnv();
|
|
2144
2157
|
const result = await this.runner.run(
|
|
2145
2158
|
this.sopsCommand,
|
|
2146
|
-
[...this.keyserviceArgs, "
|
|
2159
|
+
["decrypt", ...this.keyserviceArgs, "--output-type", fmt, filePath],
|
|
2147
2160
|
{
|
|
2148
2161
|
...env ? { env } : {}
|
|
2149
2162
|
}
|
|
@@ -2209,8 +2222,8 @@ var SopsClient = class {
|
|
|
2209
2222
|
[
|
|
2210
2223
|
"--config",
|
|
2211
2224
|
configPath,
|
|
2212
|
-
...this.keyserviceArgs,
|
|
2213
2225
|
"encrypt",
|
|
2226
|
+
...this.keyserviceArgs,
|
|
2214
2227
|
...args,
|
|
2215
2228
|
"--input-type",
|
|
2216
2229
|
fmt,
|
|
@@ -2264,7 +2277,7 @@ var SopsClient = class {
|
|
|
2264
2277
|
const env = this.buildSopsEnv();
|
|
2265
2278
|
const result = await this.runner.run(
|
|
2266
2279
|
this.sopsCommand,
|
|
2267
|
-
[...this.keyserviceArgs, "
|
|
2280
|
+
["rotate", ...this.keyserviceArgs, "-i", "--add-age", key, filePath],
|
|
2268
2281
|
{
|
|
2269
2282
|
...env ? { env } : {}
|
|
2270
2283
|
}
|
|
@@ -2288,7 +2301,7 @@ var SopsClient = class {
|
|
|
2288
2301
|
const env = this.buildSopsEnv();
|
|
2289
2302
|
const result = await this.runner.run(
|
|
2290
2303
|
this.sopsCommand,
|
|
2291
|
-
[...this.keyserviceArgs, "
|
|
2304
|
+
["rotate", ...this.keyserviceArgs, "-i", "--rm-age", key, filePath],
|
|
2292
2305
|
{
|
|
2293
2306
|
...env ? { env } : {}
|
|
2294
2307
|
}
|
|
@@ -2402,7 +2415,7 @@ var SopsClient = class {
|
|
|
2402
2415
|
if (sops.age && Array.isArray(sops.age) && sops.age.length > 0) return "age";
|
|
2403
2416
|
if (sops.kms && Array.isArray(sops.kms) && sops.kms.length > 0) {
|
|
2404
2417
|
const firstArn = sops.kms[0]?.arn;
|
|
2405
|
-
if (typeof firstArn === "string" && firstArn.startsWith("clef:")) {
|
|
2418
|
+
if (typeof firstArn === "string" && (firstArn.startsWith("clef:") || firstArn.includes("alias/clef/"))) {
|
|
2406
2419
|
return "cloud";
|
|
2407
2420
|
}
|
|
2408
2421
|
return "awskms";
|
|
@@ -2485,7 +2498,7 @@ var SopsClient = class {
|
|
|
2485
2498
|
case "cloud": {
|
|
2486
2499
|
const cloudKeyId = manifest.cloud?.keyId;
|
|
2487
2500
|
if (cloudKeyId) {
|
|
2488
|
-
args.push("--kms", cloudKeyId);
|
|
2501
|
+
args.push("--kms", cloudKeyToArn(cloudKeyId));
|
|
2489
2502
|
}
|
|
2490
2503
|
break;
|
|
2491
2504
|
}
|
|
@@ -3952,7 +3965,7 @@ var CloudClient = class {
|
|
|
3952
3965
|
response = await fetch(url, init);
|
|
3953
3966
|
} catch (retryErr) {
|
|
3954
3967
|
throw new CloudApiError(
|
|
3955
|
-
`Network error contacting Clef
|
|
3968
|
+
`Network error contacting Clef Cloud: ${retryErr.message}`,
|
|
3956
3969
|
0,
|
|
3957
3970
|
"Check your network connection and CLEF_API_URL."
|
|
3958
3971
|
);
|
|
@@ -3974,7 +3987,7 @@ var CloudClient = class {
|
|
|
3974
3987
|
buildError(response) {
|
|
3975
3988
|
const hint = response.status === 401 || response.status === 403 ? "Check your API token (--api-token or CLEF_API_TOKEN)." : response.status === 404 ? "Check your cloud.integrationId in clef.yaml." : void 0;
|
|
3976
3989
|
return new CloudApiError(
|
|
3977
|
-
`Clef
|
|
3990
|
+
`Clef Cloud API returned ${response.status} ${response.statusText}`,
|
|
3978
3991
|
response.status,
|
|
3979
3992
|
hint
|
|
3980
3993
|
);
|
|
@@ -4495,9 +4508,41 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
|
|
|
4495
4508
|
}
|
|
4496
4509
|
|
|
4497
4510
|
// src/artifact/packer.ts
|
|
4511
|
+
import * as crypto3 from "crypto";
|
|
4512
|
+
|
|
4513
|
+
// src/artifact/output.ts
|
|
4498
4514
|
import * as fs16 from "fs";
|
|
4499
4515
|
import * as path19 from "path";
|
|
4500
|
-
|
|
4516
|
+
var FilePackOutput = class {
|
|
4517
|
+
constructor(outputPath) {
|
|
4518
|
+
this.outputPath = outputPath;
|
|
4519
|
+
}
|
|
4520
|
+
async write(_artifact, json) {
|
|
4521
|
+
const outputDir = path19.dirname(this.outputPath);
|
|
4522
|
+
if (!fs16.existsSync(outputDir)) {
|
|
4523
|
+
fs16.mkdirSync(outputDir, { recursive: true });
|
|
4524
|
+
}
|
|
4525
|
+
const tmpOutput = `${this.outputPath}.tmp.${process.pid}`;
|
|
4526
|
+
fs16.writeFileSync(tmpOutput, json, "utf-8");
|
|
4527
|
+
fs16.renameSync(tmpOutput, this.outputPath);
|
|
4528
|
+
}
|
|
4529
|
+
};
|
|
4530
|
+
var MemoryPackOutput = class {
|
|
4531
|
+
_artifact = null;
|
|
4532
|
+
_json = null;
|
|
4533
|
+
async write(artifact, json) {
|
|
4534
|
+
this._artifact = artifact;
|
|
4535
|
+
this._json = json;
|
|
4536
|
+
}
|
|
4537
|
+
/** The packed artifact, or null if `write` hasn't been called. */
|
|
4538
|
+
get artifact() {
|
|
4539
|
+
return this._artifact;
|
|
4540
|
+
}
|
|
4541
|
+
/** The serialized JSON, or null if `write` hasn't been called. */
|
|
4542
|
+
get json() {
|
|
4543
|
+
return this._json;
|
|
4544
|
+
}
|
|
4545
|
+
};
|
|
4501
4546
|
|
|
4502
4547
|
// src/artifact/signer.ts
|
|
4503
4548
|
import * as crypto2 from "crypto";
|
|
@@ -4668,10 +4713,6 @@ var ArtifactPacker = class {
|
|
|
4668
4713
|
ciphertext
|
|
4669
4714
|
};
|
|
4670
4715
|
}
|
|
4671
|
-
const outputDir = path19.dirname(config.outputPath);
|
|
4672
|
-
if (!fs16.existsSync(outputDir)) {
|
|
4673
|
-
fs16.mkdirSync(outputDir, { recursive: true });
|
|
4674
|
-
}
|
|
4675
4716
|
if (config.ttl && config.ttl > 0) {
|
|
4676
4717
|
artifact.expiresAt = new Date(Date.now() + config.ttl * 1e3).toISOString();
|
|
4677
4718
|
}
|
|
@@ -4688,11 +4729,10 @@ var ArtifactPacker = class {
|
|
|
4688
4729
|
artifact.signatureAlgorithm = "ECDSA_SHA256";
|
|
4689
4730
|
}
|
|
4690
4731
|
const json = JSON.stringify(artifact, null, 2);
|
|
4691
|
-
const
|
|
4692
|
-
|
|
4693
|
-
fs16.renameSync(tmpOutput, config.outputPath);
|
|
4732
|
+
const output = config.output ?? new FilePackOutput(config.outputPath ?? "artifact.json");
|
|
4733
|
+
await output.write(artifact, json);
|
|
4694
4734
|
return {
|
|
4695
|
-
outputPath: config.outputPath,
|
|
4735
|
+
outputPath: config.outputPath ?? "",
|
|
4696
4736
|
namespaceCount: resolved.identity.namespaces.length,
|
|
4697
4737
|
keyCount: Object.keys(resolved.values).length,
|
|
4698
4738
|
artifactSize: Buffer.byteLength(json, "utf-8"),
|
|
@@ -4725,10 +4765,19 @@ function metadataMatchesTarget(meta, target) {
|
|
|
4725
4765
|
return meta.recipients.includes(target.key);
|
|
4726
4766
|
}
|
|
4727
4767
|
var BackendMigrator = class {
|
|
4728
|
-
|
|
4729
|
-
|
|
4768
|
+
/**
|
|
4769
|
+
* @param encryption - Backend used for both decrypt and encrypt (standard case).
|
|
4770
|
+
* @param matrixManager - Matrix resolver.
|
|
4771
|
+
* @param targetEncryption - Optional separate backend for encrypt. Use when migrating
|
|
4772
|
+
* from cloud (decrypt via keyservice) to another backend (encrypt via local credentials).
|
|
4773
|
+
*/
|
|
4774
|
+
constructor(encryption, matrixManager, targetEncryption) {
|
|
4730
4775
|
this.matrixManager = matrixManager;
|
|
4776
|
+
this.decryptBackend = encryption;
|
|
4777
|
+
this.encryptBackend = targetEncryption ?? encryption;
|
|
4731
4778
|
}
|
|
4779
|
+
decryptBackend;
|
|
4780
|
+
encryptBackend;
|
|
4732
4781
|
async migrate(manifest, repoRoot, options, onProgress) {
|
|
4733
4782
|
const { target, environment, dryRun, skipVerify } = options;
|
|
4734
4783
|
if (environment) {
|
|
@@ -4751,7 +4800,7 @@ var BackendMigrator = class {
|
|
|
4751
4800
|
const toMigrate = [];
|
|
4752
4801
|
const skippedFiles = [];
|
|
4753
4802
|
for (const cell of targetCells) {
|
|
4754
|
-
const meta = await this.
|
|
4803
|
+
const meta = await this.decryptBackend.getMetadata(cell.filePath);
|
|
4755
4804
|
if (metadataMatchesTarget(meta, target)) {
|
|
4756
4805
|
skippedFiles.push(cell.filePath);
|
|
4757
4806
|
onProgress?.({
|
|
@@ -4813,8 +4862,8 @@ var BackendMigrator = class {
|
|
|
4813
4862
|
file: cell.filePath,
|
|
4814
4863
|
message: `Migrating ${cell.namespace}/${cell.environment}...`
|
|
4815
4864
|
});
|
|
4816
|
-
const decrypted = await this.
|
|
4817
|
-
await this.
|
|
4865
|
+
const decrypted = await this.decryptBackend.decrypt(cell.filePath);
|
|
4866
|
+
await this.encryptBackend.encrypt(
|
|
4818
4867
|
cell.filePath,
|
|
4819
4868
|
decrypted.values,
|
|
4820
4869
|
updatedManifest,
|
|
@@ -4849,7 +4898,7 @@ var BackendMigrator = class {
|
|
|
4849
4898
|
file: cell.filePath,
|
|
4850
4899
|
message: `Verifying ${cell.namespace}/${cell.environment}...`
|
|
4851
4900
|
});
|
|
4852
|
-
await this.
|
|
4901
|
+
await this.encryptBackend.decrypt(cell.filePath);
|
|
4853
4902
|
verifiedFiles.push(cell.filePath);
|
|
4854
4903
|
} catch (err) {
|
|
4855
4904
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -4885,6 +4934,18 @@ var BackendMigrator = class {
|
|
|
4885
4934
|
sops[keyField] = target.key;
|
|
4886
4935
|
}
|
|
4887
4936
|
}
|
|
4937
|
+
if (doc.cloud && target.backend !== "cloud") {
|
|
4938
|
+
const sops = doc.sops;
|
|
4939
|
+
const environments = doc.environments;
|
|
4940
|
+
const defaultIsCloud = sops.default_backend === "cloud";
|
|
4941
|
+
const anyEnvIsCloud = environments.some((e) => {
|
|
4942
|
+
const envSops = e.sops;
|
|
4943
|
+
return envSops?.backend === "cloud";
|
|
4944
|
+
});
|
|
4945
|
+
if (!defaultIsCloud && !anyEnvIsCloud) {
|
|
4946
|
+
delete doc.cloud;
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4888
4949
|
}
|
|
4889
4950
|
rollback(manifestPath, manifestBackup, fileBackups) {
|
|
4890
4951
|
for (const [filePath, backup] of fileBackups) {
|
|
@@ -5065,11 +5126,10 @@ function readCloudCredentials() {
|
|
|
5065
5126
|
}
|
|
5066
5127
|
if (!raw || typeof raw !== "object") return null;
|
|
5067
5128
|
const obj = raw;
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
};
|
|
5129
|
+
const token = typeof obj.token === "string" && obj.token.length > 0 ? obj.token : "";
|
|
5130
|
+
const endpoint = typeof obj.endpoint === "string" ? obj.endpoint : CLOUD_DEFAULT_ENDPOINT;
|
|
5131
|
+
if (!token && endpoint === CLOUD_DEFAULT_ENDPOINT) return null;
|
|
5132
|
+
return { token, endpoint };
|
|
5073
5133
|
}
|
|
5074
5134
|
function writeCloudCredentials(credentials) {
|
|
5075
5135
|
const clefDir = path23.join(os2.homedir(), ".clef");
|
|
@@ -5085,21 +5145,30 @@ function writeCloudCredentials(credentials) {
|
|
|
5085
5145
|
// src/cloud/device-flow.ts
|
|
5086
5146
|
async function initiateDeviceFlow(endpoint, options) {
|
|
5087
5147
|
const base = endpoint ?? CLOUD_DEFAULT_ENDPOINT;
|
|
5148
|
+
const payload = {
|
|
5149
|
+
clientType: "cli",
|
|
5150
|
+
clientVersion: options.clientVersion,
|
|
5151
|
+
repoName: options.repoName,
|
|
5152
|
+
flow: options.flow
|
|
5153
|
+
};
|
|
5154
|
+
if (options.environment) {
|
|
5155
|
+
payload.environment = options.environment;
|
|
5156
|
+
}
|
|
5088
5157
|
const res = await fetch(`${base}/api/v1/device/init`, {
|
|
5089
5158
|
method: "POST",
|
|
5090
5159
|
headers: { "Content-Type": "application/json" },
|
|
5091
|
-
body: JSON.stringify(
|
|
5092
|
-
clientType: "cli",
|
|
5093
|
-
clientVersion: options.clientVersion,
|
|
5094
|
-
repoName: options.repoName,
|
|
5095
|
-
environment: options.environment
|
|
5096
|
-
})
|
|
5160
|
+
body: JSON.stringify(payload)
|
|
5097
5161
|
});
|
|
5098
5162
|
if (!res.ok) {
|
|
5099
5163
|
const body = await res.text().catch(() => "");
|
|
5100
5164
|
throw new Error(`Device flow init failed (${res.status}): ${body}`);
|
|
5101
5165
|
}
|
|
5102
|
-
|
|
5166
|
+
const json = await res.json();
|
|
5167
|
+
const session = json.data ?? json;
|
|
5168
|
+
if (session.pollUrl && !session.pollUrl.startsWith("http")) {
|
|
5169
|
+
session.pollUrl = `${base}${session.pollUrl}`;
|
|
5170
|
+
}
|
|
5171
|
+
return session;
|
|
5103
5172
|
}
|
|
5104
5173
|
async function pollDeviceFlow(pollUrl) {
|
|
5105
5174
|
const res = await fetch(pollUrl);
|
|
@@ -5107,7 +5176,8 @@ async function pollDeviceFlow(pollUrl) {
|
|
|
5107
5176
|
const body = await res.text().catch(() => "");
|
|
5108
5177
|
throw new Error(`Device flow poll failed (${res.status}): ${body}`);
|
|
5109
5178
|
}
|
|
5110
|
-
|
|
5179
|
+
const json = await res.json();
|
|
5180
|
+
return json.data ?? json;
|
|
5111
5181
|
}
|
|
5112
5182
|
|
|
5113
5183
|
// src/cloud/pack-client.ts
|
|
@@ -5154,7 +5224,6 @@ var CloudArtifactClient = class {
|
|
|
5154
5224
|
this.endpoint = endpoint ?? CLOUD_DEFAULT_ENDPOINT;
|
|
5155
5225
|
}
|
|
5156
5226
|
async upload(token, config) {
|
|
5157
|
-
const content = fs21.readFileSync(config.artifactPath, "utf-8");
|
|
5158
5227
|
const res = await fetch(
|
|
5159
5228
|
`${this.endpoint}/api/v1/cloud/artifacts/${config.identity}/${config.environment}`,
|
|
5160
5229
|
{
|
|
@@ -5163,7 +5232,7 @@ var CloudArtifactClient = class {
|
|
|
5163
5232
|
Authorization: `Bearer ${token}`,
|
|
5164
5233
|
"Content-Type": "application/json"
|
|
5165
5234
|
},
|
|
5166
|
-
body:
|
|
5235
|
+
body: config.artifactJson
|
|
5167
5236
|
}
|
|
5168
5237
|
);
|
|
5169
5238
|
if (!res.ok) {
|
|
@@ -5187,6 +5256,7 @@ export {
|
|
|
5187
5256
|
ConsumptionClient,
|
|
5188
5257
|
DiffEngine,
|
|
5189
5258
|
DriftDetector,
|
|
5259
|
+
FilePackOutput,
|
|
5190
5260
|
GitIntegration,
|
|
5191
5261
|
GitOperationError,
|
|
5192
5262
|
ImportRunner,
|
|
@@ -5194,6 +5264,7 @@ export {
|
|
|
5194
5264
|
ManifestParser,
|
|
5195
5265
|
ManifestValidationError,
|
|
5196
5266
|
MatrixManager,
|
|
5267
|
+
MemoryPackOutput,
|
|
5197
5268
|
PartialRotationError,
|
|
5198
5269
|
REQUESTS_FILENAME,
|
|
5199
5270
|
REQUIREMENTS,
|