@clef-sh/core 0.1.27 → 0.1.28
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/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -9
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +119 -9
- package/dist/index.mjs.map +3 -3
- package/dist/kms/aws-arn.d.ts +29 -0
- package/dist/kms/aws-arn.d.ts.map +1 -0
- package/dist/kms/index.d.ts +2 -0
- package/dist/kms/index.d.ts.map +1 -1
- package/dist/manifest/io.d.ts +6 -0
- package/dist/manifest/io.d.ts.map +1 -1
- package/dist/manifest/parser.d.ts.map +1 -1
- package/dist/migration/backend.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2269,11 +2269,98 @@ function keyPreview(key) {
|
|
|
2269
2269
|
return `age1\u2026${last8}`;
|
|
2270
2270
|
}
|
|
2271
2271
|
|
|
2272
|
+
// src/kms/aws-arn.ts
|
|
2273
|
+
var PARTITION_PATTERN = /^aws(?:-[a-z]+)*$/;
|
|
2274
|
+
var REGION_PATTERN = /^[a-z]{2,}(?:-[a-z]+)+-\d+$/;
|
|
2275
|
+
var ACCOUNT_PATTERN = /^\d{12}$/;
|
|
2276
|
+
function validateAwsKmsArn(input) {
|
|
2277
|
+
if (typeof input !== "string") {
|
|
2278
|
+
return { ok: false, reason: "value must be a string" };
|
|
2279
|
+
}
|
|
2280
|
+
if (input.length === 0) {
|
|
2281
|
+
return { ok: false, reason: "value is empty" };
|
|
2282
|
+
}
|
|
2283
|
+
if (!input.startsWith("arn:")) {
|
|
2284
|
+
return {
|
|
2285
|
+
ok: false,
|
|
2286
|
+
reason: "expected an ARN starting with 'arn:' (got a bare key id, alias name, or other format). Use a full ARN like 'arn:aws:kms:us-east-1:123456789012:alias/<name>'."
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
const segments = input.split(":");
|
|
2290
|
+
if (segments.length < 6) {
|
|
2291
|
+
return {
|
|
2292
|
+
ok: false,
|
|
2293
|
+
reason: `expected 6 colon-delimited segments (arn:aws:kms:<region>:<account>:<resource>), got ${segments.length}. Check that the region and account aren't missing.`
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
if (segments.length > 6) {
|
|
2297
|
+
return {
|
|
2298
|
+
ok: false,
|
|
2299
|
+
reason: `expected exactly 6 colon-delimited segments, got ${segments.length}. Check for stray ':' characters.`
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
const [, partition, service, region, account, resource] = segments;
|
|
2303
|
+
if (!PARTITION_PATTERN.test(partition)) {
|
|
2304
|
+
return {
|
|
2305
|
+
ok: false,
|
|
2306
|
+
reason: `partition segment '${partition}' is not recognized. Expected 'aws', 'aws-us-gov', 'aws-cn', etc.`
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
if (service !== "kms") {
|
|
2310
|
+
return {
|
|
2311
|
+
ok: false,
|
|
2312
|
+
reason: `service segment must be 'kms', got '${service}'.`
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
if (region.length === 0) {
|
|
2316
|
+
return {
|
|
2317
|
+
ok: false,
|
|
2318
|
+
reason: "region segment is empty (look for '::' between 'kms' and the account id). Set a region like 'us-east-1' before reconstructing the ARN \u2014 common cause: a $REGION shell variable was unset when the ARN was built."
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
if (!REGION_PATTERN.test(region)) {
|
|
2322
|
+
return {
|
|
2323
|
+
ok: false,
|
|
2324
|
+
reason: `region segment '${region}' doesn't look like an AWS region (expected e.g. 'us-east-1', 'eu-west-2').`
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
if (account.length === 0) {
|
|
2328
|
+
return {
|
|
2329
|
+
ok: false,
|
|
2330
|
+
reason: "account segment is empty. Provide the 12-digit AWS account id."
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
if (!ACCOUNT_PATTERN.test(account)) {
|
|
2334
|
+
return {
|
|
2335
|
+
ok: false,
|
|
2336
|
+
reason: `account segment '${account}' must be exactly 12 digits.`
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
if (!resource || resource.length === 0) {
|
|
2340
|
+
return {
|
|
2341
|
+
ok: false,
|
|
2342
|
+
reason: "resource segment is empty. Expected 'key/<id>' or 'alias/<name>' after the account."
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
if (!resource.startsWith("key/") && !resource.startsWith("alias/")) {
|
|
2346
|
+
return {
|
|
2347
|
+
ok: false,
|
|
2348
|
+
reason: `resource '${resource}' must start with 'key/' or 'alias/'.`
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
if (resource === "key/" || resource === "alias/") {
|
|
2352
|
+
return {
|
|
2353
|
+
ok: false,
|
|
2354
|
+
reason: "resource id is empty after 'key/' or 'alias/'."
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
return { ok: true };
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2272
2360
|
// src/manifest/parser.ts
|
|
2273
2361
|
var CLEF_MANIFEST_FILENAME = "clef.yaml";
|
|
2274
2362
|
var VALID_BACKENDS = ["age", "awskms", "gcpkms", "azurekv", "pgp", "hsm"];
|
|
2275
2363
|
var PKCS11_URI_PATTERN = /^pkcs11:[a-zA-Z][a-zA-Z0-9_-]*=[^;]+/;
|
|
2276
|
-
var AWS_KMS_ARN_PATTERN = /^arn:aws(?:-[a-z]+)*:kms:[a-z0-9-]+:\d+:(key|alias)\/.+$/;
|
|
2277
2364
|
var VALID_TOP_LEVEL_KEYS = [
|
|
2278
2365
|
"version",
|
|
2279
2366
|
"environments",
|
|
@@ -2796,11 +2883,14 @@ var ManifestParser = class {
|
|
|
2796
2883
|
"service_identities"
|
|
2797
2884
|
);
|
|
2798
2885
|
}
|
|
2799
|
-
if (kmsObj.provider === "aws"
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2886
|
+
if (kmsObj.provider === "aws") {
|
|
2887
|
+
const arnValidation = validateAwsKmsArn(kmsObj.keyId);
|
|
2888
|
+
if (!arnValidation.ok) {
|
|
2889
|
+
throw new ManifestValidationError(
|
|
2890
|
+
`Service identity '${siName}' environment '${envName}': kms.keyId is not a valid AWS KMS ARN \u2014 ${arnValidation.reason} (got '${kmsObj.keyId}'). Expected shape: arn:aws:kms:<region>:<account>:key/<id> or arn:aws:kms:<region>:<account>:alias/<name>.`,
|
|
2891
|
+
"service_identities"
|
|
2892
|
+
);
|
|
2893
|
+
}
|
|
2804
2894
|
}
|
|
2805
2895
|
if (Object.prototype.hasOwnProperty.call(kmsObj, "region")) {
|
|
2806
2896
|
throw new ManifestValidationError(
|
|
@@ -2875,6 +2965,18 @@ function readManifestYaml(repoRoot) {
|
|
|
2875
2965
|
return YAML2.parse(raw);
|
|
2876
2966
|
}
|
|
2877
2967
|
function writeManifestYaml(repoRoot, doc) {
|
|
2968
|
+
const parser = new ManifestParser();
|
|
2969
|
+
try {
|
|
2970
|
+
parser.validate(doc);
|
|
2971
|
+
} catch (err) {
|
|
2972
|
+
if (err instanceof ManifestValidationError) {
|
|
2973
|
+
throw new ManifestValidationError(
|
|
2974
|
+
`Refusing to write invalid manifest: ${err.message}`,
|
|
2975
|
+
err.field
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
throw err;
|
|
2979
|
+
}
|
|
2878
2980
|
const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
2879
2981
|
import_write_file_atomic.default.sync(manifestPath, YAML2.stringify(doc));
|
|
2880
2982
|
}
|
|
@@ -8933,6 +9035,8 @@ var BackendMigrator = class {
|
|
|
8933
9035
|
warnings: ["All files already use the target backend and key. Nothing to migrate."]
|
|
8934
9036
|
};
|
|
8935
9037
|
}
|
|
9038
|
+
const preMigrationWarnings = [];
|
|
9039
|
+
this.checkAgeRecipientsWarning(manifest, target, environment, preMigrationWarnings);
|
|
8936
9040
|
if (dryRun) {
|
|
8937
9041
|
const warnings2 = [];
|
|
8938
9042
|
for (const cell of toMigrate) {
|
|
@@ -8949,7 +9053,7 @@ var BackendMigrator = class {
|
|
|
8949
9053
|
} else {
|
|
8950
9054
|
warnings2.push(`Would update global default_backend \u2192 ${target.backend}`);
|
|
8951
9055
|
}
|
|
8952
|
-
|
|
9056
|
+
warnings2.push(...preMigrationWarnings);
|
|
8953
9057
|
return {
|
|
8954
9058
|
migratedFiles: [],
|
|
8955
9059
|
skippedFiles,
|
|
@@ -9005,7 +9109,12 @@ var BackendMigrator = class {
|
|
|
9005
9109
|
rolledBack: true,
|
|
9006
9110
|
error: migrationError.message,
|
|
9007
9111
|
verifiedFiles: [],
|
|
9008
|
-
warnings
|
|
9112
|
+
// Surface pre-migration warnings even on rollback. The new manifest
|
|
9113
|
+
// validator can reject the write (e.g. per-env recipients vs.
|
|
9114
|
+
// non-age backend), and without these warnings the user only sees
|
|
9115
|
+
// an opaque "rolled back" message — not the actionable hint about
|
|
9116
|
+
// what to clean up first.
|
|
9117
|
+
warnings: ["All changes have been rolled back.", ...preMigrationWarnings]
|
|
9009
9118
|
};
|
|
9010
9119
|
}
|
|
9011
9120
|
const verifiedFiles = [];
|
|
@@ -9028,7 +9137,7 @@ var BackendMigrator = class {
|
|
|
9028
9137
|
}
|
|
9029
9138
|
}
|
|
9030
9139
|
}
|
|
9031
|
-
|
|
9140
|
+
warnings.push(...preMigrationWarnings);
|
|
9032
9141
|
return { migratedFiles, skippedFiles, rolledBack: false, verifiedFiles, warnings };
|
|
9033
9142
|
}
|
|
9034
9143
|
// ── Private helpers ──────────────────────────────────────────────────
|
|
@@ -9862,6 +9971,7 @@ export {
|
|
|
9862
9971
|
tryBundledKeyservice,
|
|
9863
9972
|
upsertRequest,
|
|
9864
9973
|
validateAgePublicKey,
|
|
9974
|
+
validateAwsKmsArn,
|
|
9865
9975
|
validatePackedArtifact,
|
|
9866
9976
|
validateResetScope,
|
|
9867
9977
|
verifySignature,
|