@attest-it/cli 0.7.0 → 0.8.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/dist/bin/attest-it.js +113 -21
- package/dist/bin/attest-it.js.map +1 -1
- package/dist/index.cjs +112 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +113 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -363,7 +363,7 @@ async function runStatus(gates, options) {
|
|
|
363
363
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
364
364
|
}
|
|
365
365
|
const projectRoot = process.cwd();
|
|
366
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
366
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
367
367
|
const gatesToCheck = gates.length > 0 ? gates : Object.keys(attestItConfig.gates);
|
|
368
368
|
for (const gateId of gatesToCheck) {
|
|
369
369
|
if (!attestItConfig.gates[gateId]) {
|
|
@@ -1015,12 +1015,24 @@ async function getAllSuiteStatuses(config) {
|
|
|
1015
1015
|
const attestations = attestationsFile?.attestations ?? [];
|
|
1016
1016
|
const results = [];
|
|
1017
1017
|
for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
|
|
1018
|
-
|
|
1018
|
+
let packages;
|
|
1019
|
+
let ignore;
|
|
1020
|
+
if (suiteConfig.gate && config.gates) {
|
|
1021
|
+
const gateConfig = config.gates[suiteConfig.gate];
|
|
1022
|
+
if (gateConfig) {
|
|
1023
|
+
packages = gateConfig.fingerprint.paths;
|
|
1024
|
+
ignore = gateConfig.fingerprint.exclude;
|
|
1025
|
+
}
|
|
1026
|
+
} else if (suiteConfig.packages) {
|
|
1027
|
+
packages = suiteConfig.packages;
|
|
1028
|
+
ignore = suiteConfig.ignore;
|
|
1029
|
+
}
|
|
1030
|
+
if (!packages || packages.length === 0) {
|
|
1019
1031
|
continue;
|
|
1020
1032
|
}
|
|
1021
1033
|
const fingerprintResult = await core.computeFingerprint({
|
|
1022
|
-
packages
|
|
1023
|
-
...
|
|
1034
|
+
packages,
|
|
1035
|
+
...ignore && { ignore }
|
|
1024
1036
|
});
|
|
1025
1037
|
const attestation = core.findAttestation(
|
|
1026
1038
|
{ schemaVersion: "1", attestations, signature: "" },
|
|
@@ -1585,18 +1597,19 @@ async function promptForSeal(suiteName, gateId, config) {
|
|
|
1585
1597
|
const fs4 = await import('fs/promises');
|
|
1586
1598
|
const privateKeyPem = await fs4.readFile(keyResult.keyPath, "utf8");
|
|
1587
1599
|
await keyResult.cleanup();
|
|
1600
|
+
const identitySlug = localConfig.activeIdentity;
|
|
1588
1601
|
const seal = core.createSeal({
|
|
1589
1602
|
gateId,
|
|
1590
1603
|
fingerprint: gateFingerprint.fingerprint,
|
|
1591
|
-
sealedBy:
|
|
1604
|
+
sealedBy: identitySlug,
|
|
1592
1605
|
privateKey: privateKeyPem
|
|
1593
1606
|
});
|
|
1594
1607
|
const projectRoot = process.cwd();
|
|
1595
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
1608
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
1596
1609
|
sealsFile.seals[gateId] = seal;
|
|
1597
|
-
core.writeSealsSync(projectRoot, sealsFile);
|
|
1610
|
+
core.writeSealsSync(projectRoot, sealsFile, attestItConfig.settings.sealsPath);
|
|
1598
1611
|
success(`Seal created for gate '${gateId}'`);
|
|
1599
|
-
log(` Sealed by: ${identity.name}`);
|
|
1612
|
+
log(` Sealed by: ${identitySlug} (${identity.name})`);
|
|
1600
1613
|
log(` Timestamp: ${seal.timestamp}`);
|
|
1601
1614
|
} catch (err) {
|
|
1602
1615
|
if (err instanceof Error) {
|
|
@@ -1664,6 +1677,7 @@ function getKeyRefFromIdentity(identity) {
|
|
|
1664
1677
|
}
|
|
1665
1678
|
}
|
|
1666
1679
|
}
|
|
1680
|
+
var MIN_PASSPHRASE_LENGTH = 8;
|
|
1667
1681
|
function KeygenInteractive(props) {
|
|
1668
1682
|
const { onComplete, onCancel, onError } = props;
|
|
1669
1683
|
const [step, setStep] = React7.useState("checking-providers");
|
|
@@ -1682,6 +1696,7 @@ function KeygenInteractive(props) {
|
|
|
1682
1696
|
const [selectedYubiKeySlot, setSelectedYubiKeySlot] = React7.useState(2);
|
|
1683
1697
|
const [slot1Configured, setSlot1Configured] = React7.useState(false);
|
|
1684
1698
|
const [slot2Configured, setSlot2Configured] = React7.useState(false);
|
|
1699
|
+
const [encryptionPassphrase, setEncryptionPassphrase] = React7.useState();
|
|
1685
1700
|
ink.useInput((_input, key) => {
|
|
1686
1701
|
if (key.escape) {
|
|
1687
1702
|
onCancel();
|
|
@@ -1755,7 +1770,7 @@ function KeygenInteractive(props) {
|
|
|
1755
1770
|
const handleProviderSelect = (value) => {
|
|
1756
1771
|
if (value === "filesystem") {
|
|
1757
1772
|
setSelectedProvider("filesystem");
|
|
1758
|
-
|
|
1773
|
+
setStep("select-filesystem-encryption");
|
|
1759
1774
|
} else if (value === "1password") {
|
|
1760
1775
|
setSelectedProvider("1password");
|
|
1761
1776
|
if (accounts.length === 1 && accounts[0]) {
|
|
@@ -1809,6 +1824,31 @@ function KeygenInteractive(props) {
|
|
|
1809
1824
|
onError(new Error("YubiKey setup cancelled"));
|
|
1810
1825
|
}
|
|
1811
1826
|
};
|
|
1827
|
+
const handleEncryptionMethodSelect = (value) => {
|
|
1828
|
+
if (value === "passphrase") {
|
|
1829
|
+
setStep("enter-encryption-passphrase");
|
|
1830
|
+
} else {
|
|
1831
|
+
setEncryptionPassphrase(void 0);
|
|
1832
|
+
void generateKeys("filesystem");
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
const handleEncryptionPassphrase = (value) => {
|
|
1836
|
+
if (value.length < MIN_PASSPHRASE_LENGTH) {
|
|
1837
|
+
onError(new Error(`Passphrase must be at least ${String(MIN_PASSPHRASE_LENGTH)} characters`));
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
setEncryptionPassphrase(value);
|
|
1841
|
+
setStep("confirm-encryption-passphrase");
|
|
1842
|
+
};
|
|
1843
|
+
const handleConfirmPassphrase = (value) => {
|
|
1844
|
+
if (value !== encryptionPassphrase) {
|
|
1845
|
+
onError(new Error("Passphrases do not match. Please try again."));
|
|
1846
|
+
setEncryptionPassphrase(void 0);
|
|
1847
|
+
setStep("enter-encryption-passphrase");
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
void generateKeys("filesystem");
|
|
1851
|
+
};
|
|
1812
1852
|
const setupYubiKeySlot = async () => {
|
|
1813
1853
|
setStep("yubikey-configuring");
|
|
1814
1854
|
try {
|
|
@@ -1840,17 +1880,26 @@ function KeygenInteractive(props) {
|
|
|
1840
1880
|
const publicKeyPath = props.publicKeyPath ?? core.getDefaultPublicKeyPath();
|
|
1841
1881
|
if (provider === "filesystem") {
|
|
1842
1882
|
const fsProvider = new core.FilesystemKeyProvider();
|
|
1843
|
-
const genOptions = {
|
|
1883
|
+
const genOptions = {
|
|
1884
|
+
publicKeyPath
|
|
1885
|
+
};
|
|
1844
1886
|
if (props.force !== void 0) {
|
|
1845
1887
|
genOptions.force = props.force;
|
|
1846
1888
|
}
|
|
1889
|
+
if (encryptionPassphrase !== void 0) {
|
|
1890
|
+
genOptions.passphrase = encryptionPassphrase;
|
|
1891
|
+
}
|
|
1847
1892
|
const result = await fsProvider.generateKeyPair(genOptions);
|
|
1848
|
-
|
|
1893
|
+
const completionResult = {
|
|
1849
1894
|
provider: "filesystem",
|
|
1850
1895
|
publicKeyPath: result.publicKeyPath,
|
|
1851
1896
|
privateKeyRef: result.privateKeyRef,
|
|
1852
1897
|
storageDescription: result.storageDescription
|
|
1853
|
-
}
|
|
1898
|
+
};
|
|
1899
|
+
if (result.encrypted) {
|
|
1900
|
+
completionResult.encrypted = result.encrypted;
|
|
1901
|
+
}
|
|
1902
|
+
onComplete(completionResult);
|
|
1854
1903
|
} else if (provider === "1password") {
|
|
1855
1904
|
if (!selectedVault || !itemName) {
|
|
1856
1905
|
throw new Error("Vault and item name are required for 1Password");
|
|
@@ -1934,6 +1983,8 @@ function KeygenInteractive(props) {
|
|
|
1934
1983
|
setStep("done");
|
|
1935
1984
|
} catch (err) {
|
|
1936
1985
|
onError(err instanceof Error ? err : new Error("Key generation failed"));
|
|
1986
|
+
} finally {
|
|
1987
|
+
setEncryptionPassphrase(void 0);
|
|
1937
1988
|
}
|
|
1938
1989
|
};
|
|
1939
1990
|
if (step === "checking-providers") {
|
|
@@ -1973,6 +2024,39 @@ function KeygenInteractive(props) {
|
|
|
1973
2024
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleProviderSelect })
|
|
1974
2025
|
] });
|
|
1975
2026
|
}
|
|
2027
|
+
if (step === "select-filesystem-encryption") {
|
|
2028
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2029
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Would you like to encrypt your private key with a passphrase?" }),
|
|
2030
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "A passphrase adds extra security but must be entered each time you sign." }),
|
|
2031
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2032
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2033
|
+
ui.Select,
|
|
2034
|
+
{
|
|
2035
|
+
options: [
|
|
2036
|
+
{ label: "No encryption (key protected by file permissions only)", value: "none" },
|
|
2037
|
+
{ label: "Passphrase protection (AES-256 encryption)", value: "passphrase" }
|
|
2038
|
+
],
|
|
2039
|
+
onChange: handleEncryptionMethodSelect
|
|
2040
|
+
}
|
|
2041
|
+
)
|
|
2042
|
+
] });
|
|
2043
|
+
}
|
|
2044
|
+
if (step === "enter-encryption-passphrase") {
|
|
2045
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2046
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Enter a passphrase to encrypt your private key:" }),
|
|
2047
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: `(Minimum ${String(MIN_PASSPHRASE_LENGTH)} characters. You will need this passphrase each time you sign.)` }),
|
|
2048
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2049
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.PasswordInput, { onSubmit: handleEncryptionPassphrase })
|
|
2050
|
+
] });
|
|
2051
|
+
}
|
|
2052
|
+
if (step === "confirm-encryption-passphrase") {
|
|
2053
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2054
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Confirm your passphrase:" }),
|
|
2055
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(Enter the same passphrase again to confirm.)" }),
|
|
2056
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2057
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.PasswordInput, { onSubmit: handleConfirmPassphrase })
|
|
2058
|
+
] });
|
|
2059
|
+
}
|
|
1976
2060
|
if (step === "select-account") {
|
|
1977
2061
|
const options = accounts.map((account) => ({
|
|
1978
2062
|
label: account.name ? `${account.name} (${account.email})` : account.email,
|
|
@@ -2388,7 +2472,7 @@ async function runVerify(gates, options) {
|
|
|
2388
2472
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2389
2473
|
}
|
|
2390
2474
|
const projectRoot = process.cwd();
|
|
2391
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
2475
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
2392
2476
|
const gatesToVerify = gates.length > 0 ? gates : Object.keys(attestItConfig.gates);
|
|
2393
2477
|
for (const gateId of gatesToVerify) {
|
|
2394
2478
|
if (!attestItConfig.gates[gateId]) {
|
|
@@ -2544,7 +2628,7 @@ async function runSeal(gates, options) {
|
|
|
2544
2628
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2545
2629
|
}
|
|
2546
2630
|
const projectRoot = process.cwd();
|
|
2547
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
2631
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
2548
2632
|
const gatesToSeal = gates.length > 0 ? gates : getAllGateIds(attestItConfig);
|
|
2549
2633
|
for (const gateId of gatesToSeal) {
|
|
2550
2634
|
if (!attestItConfig.gates[gateId]) {
|
|
@@ -2557,9 +2641,17 @@ async function runSeal(gates, options) {
|
|
|
2557
2641
|
skipped: [],
|
|
2558
2642
|
failed: []
|
|
2559
2643
|
};
|
|
2644
|
+
const identitySlug = localConfig.activeIdentity;
|
|
2560
2645
|
for (const gateId of gatesToSeal) {
|
|
2561
2646
|
try {
|
|
2562
|
-
const result = await processSingleGate(
|
|
2647
|
+
const result = await processSingleGate(
|
|
2648
|
+
gateId,
|
|
2649
|
+
attestItConfig,
|
|
2650
|
+
identity,
|
|
2651
|
+
identitySlug,
|
|
2652
|
+
sealsFile,
|
|
2653
|
+
options
|
|
2654
|
+
);
|
|
2563
2655
|
if (result.sealed) {
|
|
2564
2656
|
summary.sealed.push(gateId);
|
|
2565
2657
|
} else if (result.skipped) {
|
|
@@ -2571,7 +2663,7 @@ async function runSeal(gates, options) {
|
|
|
2571
2663
|
}
|
|
2572
2664
|
}
|
|
2573
2665
|
if (!options.dryRun && summary.sealed.length > 0) {
|
|
2574
|
-
core.writeSealsSync(projectRoot, sealsFile);
|
|
2666
|
+
core.writeSealsSync(projectRoot, sealsFile, attestItConfig.settings.sealsPath);
|
|
2575
2667
|
}
|
|
2576
2668
|
displaySummary(summary, options.dryRun);
|
|
2577
2669
|
if (summary.failed.length > 0) {
|
|
@@ -2590,7 +2682,7 @@ async function runSeal(gates, options) {
|
|
|
2590
2682
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2591
2683
|
}
|
|
2592
2684
|
}
|
|
2593
|
-
async function processSingleGate(gateId, config, identity, sealsFile, options) {
|
|
2685
|
+
async function processSingleGate(gateId, config, identity, identitySlug, sealsFile, options) {
|
|
2594
2686
|
verbose(`Processing gate: ${gateId}`);
|
|
2595
2687
|
const gate = core.getGate(config, gateId);
|
|
2596
2688
|
if (!gate) {
|
|
@@ -2630,12 +2722,12 @@ async function processSingleGate(gateId, config, identity, sealsFile, options) {
|
|
|
2630
2722
|
const seal = core.createSeal({
|
|
2631
2723
|
gateId,
|
|
2632
2724
|
fingerprint: fingerprintResult.fingerprint,
|
|
2633
|
-
sealedBy:
|
|
2725
|
+
sealedBy: identitySlug,
|
|
2634
2726
|
privateKey: privateKeyPem
|
|
2635
2727
|
});
|
|
2636
2728
|
sealsFile.seals[gateId] = seal;
|
|
2637
2729
|
log(` Sealed gate: ${gateId}`);
|
|
2638
|
-
verbose(` Sealed by: ${identity.name}`);
|
|
2730
|
+
verbose(` Sealed by: ${identitySlug} (${identity.name})`);
|
|
2639
2731
|
verbose(` Timestamp: ${seal.timestamp}`);
|
|
2640
2732
|
return { sealed: true, skipped: false };
|
|
2641
2733
|
}
|
|
@@ -4072,7 +4164,7 @@ async function runRemove2(slug, options) {
|
|
|
4072
4164
|
const projectRoot = process.cwd();
|
|
4073
4165
|
let sealsFile;
|
|
4074
4166
|
try {
|
|
4075
|
-
sealsFile = core.readSealsSync(projectRoot);
|
|
4167
|
+
sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
4076
4168
|
} catch {
|
|
4077
4169
|
sealsFile = { version: 1, seals: {} };
|
|
4078
4170
|
}
|