@attest-it/cli 0.6.1 → 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 +310 -25
- package/dist/bin/attest-it.js.map +1 -1
- package/dist/index.cjs +308 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +310 -25
- 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,18 +1677,31 @@ function getKeyRefFromIdentity(identity) {
|
|
|
1664
1677
|
}
|
|
1665
1678
|
}
|
|
1666
1679
|
}
|
|
1680
|
+
var MIN_PASSPHRASE_LENGTH = 8;
|
|
1667
1681
|
function KeygenInteractive(props) {
|
|
1668
|
-
const { onComplete, onError } = props;
|
|
1682
|
+
const { onComplete, onCancel, onError } = props;
|
|
1669
1683
|
const [step, setStep] = React7.useState("checking-providers");
|
|
1670
1684
|
const [opAvailable, setOpAvailable] = React7.useState(false);
|
|
1671
1685
|
const [keychainAvailable, setKeychainAvailable] = React7.useState(false);
|
|
1686
|
+
const [yubiKeyAvailable, setYubiKeyAvailable] = React7.useState(false);
|
|
1672
1687
|
const [accounts, setAccounts] = React7.useState([]);
|
|
1673
1688
|
const [vaults, setVaults] = React7.useState([]);
|
|
1689
|
+
const [yubiKeyDevices, setYubiKeyDevices] = React7.useState([]);
|
|
1674
1690
|
const [_selectedProvider, setSelectedProvider] = React7.useState();
|
|
1675
1691
|
const [selectedAccount, setSelectedAccount] = React7.useState();
|
|
1676
1692
|
const [selectedVault, setSelectedVault] = React7.useState();
|
|
1677
1693
|
const [itemName, setItemName] = React7.useState("attest-it-private-key");
|
|
1678
1694
|
const [keychainItemName, setKeychainItemName] = React7.useState("attest-it-private-key");
|
|
1695
|
+
const [selectedYubiKeySerial, setSelectedYubiKeySerial] = React7.useState();
|
|
1696
|
+
const [selectedYubiKeySlot, setSelectedYubiKeySlot] = React7.useState(2);
|
|
1697
|
+
const [slot1Configured, setSlot1Configured] = React7.useState(false);
|
|
1698
|
+
const [slot2Configured, setSlot2Configured] = React7.useState(false);
|
|
1699
|
+
const [encryptionPassphrase, setEncryptionPassphrase] = React7.useState();
|
|
1700
|
+
ink.useInput((_input, key) => {
|
|
1701
|
+
if (key.escape) {
|
|
1702
|
+
onCancel();
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1679
1705
|
React7.useEffect(() => {
|
|
1680
1706
|
const checkProviders = async () => {
|
|
1681
1707
|
try {
|
|
@@ -1690,6 +1716,19 @@ function KeygenInteractive(props) {
|
|
|
1690
1716
|
}
|
|
1691
1717
|
const isKeychainAvailable = core.MacOSKeychainKeyProvider.isAvailable();
|
|
1692
1718
|
setKeychainAvailable(isKeychainAvailable);
|
|
1719
|
+
try {
|
|
1720
|
+
const isInstalled = await core.YubiKeyProvider.isInstalled();
|
|
1721
|
+
if (isInstalled) {
|
|
1722
|
+
const isConnected = await core.YubiKeyProvider.isConnected();
|
|
1723
|
+
if (isConnected) {
|
|
1724
|
+
const devices = await core.YubiKeyProvider.listDevices();
|
|
1725
|
+
setYubiKeyDevices(devices);
|
|
1726
|
+
setYubiKeyAvailable(devices.length > 0);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
} catch {
|
|
1730
|
+
setYubiKeyAvailable(false);
|
|
1731
|
+
}
|
|
1693
1732
|
setStep("select-provider");
|
|
1694
1733
|
};
|
|
1695
1734
|
void checkProviders();
|
|
@@ -1707,10 +1746,31 @@ function KeygenInteractive(props) {
|
|
|
1707
1746
|
void fetchVaults();
|
|
1708
1747
|
}
|
|
1709
1748
|
}, [step, selectedAccount, onError]);
|
|
1749
|
+
const checkYubiKeySlots = async (serial) => {
|
|
1750
|
+
try {
|
|
1751
|
+
const slot1 = await core.YubiKeyProvider.isChallengeResponseConfigured(1, serial);
|
|
1752
|
+
const slot2 = await core.YubiKeyProvider.isChallengeResponseConfigured(2, serial);
|
|
1753
|
+
setSlot1Configured(slot1);
|
|
1754
|
+
setSlot2Configured(slot2);
|
|
1755
|
+
if (slot1 && slot2) {
|
|
1756
|
+
setStep("select-yubikey-slot");
|
|
1757
|
+
} else if (slot2) {
|
|
1758
|
+
setSelectedYubiKeySlot(2);
|
|
1759
|
+
void generateKeys("yubikey");
|
|
1760
|
+
} else if (slot1) {
|
|
1761
|
+
setSelectedYubiKeySlot(1);
|
|
1762
|
+
void generateKeys("yubikey");
|
|
1763
|
+
} else {
|
|
1764
|
+
setStep("yubikey-offer-setup");
|
|
1765
|
+
}
|
|
1766
|
+
} catch (err) {
|
|
1767
|
+
onError(err instanceof Error ? err : new Error("Failed to check YubiKey slots"));
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1710
1770
|
const handleProviderSelect = (value) => {
|
|
1711
1771
|
if (value === "filesystem") {
|
|
1712
1772
|
setSelectedProvider("filesystem");
|
|
1713
|
-
|
|
1773
|
+
setStep("select-filesystem-encryption");
|
|
1714
1774
|
} else if (value === "1password") {
|
|
1715
1775
|
setSelectedProvider("1password");
|
|
1716
1776
|
if (accounts.length === 1 && accounts[0]) {
|
|
@@ -1722,6 +1782,14 @@ function KeygenInteractive(props) {
|
|
|
1722
1782
|
} else if (value === "macos-keychain") {
|
|
1723
1783
|
setSelectedProvider("macos-keychain");
|
|
1724
1784
|
setStep("enter-keychain-item-name");
|
|
1785
|
+
} else if (value === "yubikey") {
|
|
1786
|
+
setSelectedProvider("yubikey");
|
|
1787
|
+
if (yubiKeyDevices.length > 1) {
|
|
1788
|
+
setStep("select-yubikey-device");
|
|
1789
|
+
} else if (yubiKeyDevices.length === 1 && yubiKeyDevices[0]) {
|
|
1790
|
+
setSelectedYubiKeySerial(yubiKeyDevices[0].serial);
|
|
1791
|
+
void checkYubiKeySlots(yubiKeyDevices[0].serial);
|
|
1792
|
+
}
|
|
1725
1793
|
}
|
|
1726
1794
|
};
|
|
1727
1795
|
const handleAccountSelect = (value) => {
|
|
@@ -1740,23 +1808,98 @@ function KeygenInteractive(props) {
|
|
|
1740
1808
|
setKeychainItemName(value);
|
|
1741
1809
|
void generateKeys("macos-keychain");
|
|
1742
1810
|
};
|
|
1811
|
+
const handleYubiKeyDeviceSelect = (value) => {
|
|
1812
|
+
setSelectedYubiKeySerial(value);
|
|
1813
|
+
void checkYubiKeySlots(value);
|
|
1814
|
+
};
|
|
1815
|
+
const handleYubiKeySlotSelect = (value) => {
|
|
1816
|
+
const slot = value === "1" ? 1 : 2;
|
|
1817
|
+
setSelectedYubiKeySlot(slot);
|
|
1818
|
+
void generateKeys("yubikey");
|
|
1819
|
+
};
|
|
1820
|
+
const handleYubiKeySetupConfirm = (value) => {
|
|
1821
|
+
if (value === "yes") {
|
|
1822
|
+
void setupYubiKeySlot();
|
|
1823
|
+
} else {
|
|
1824
|
+
onError(new Error("YubiKey setup cancelled"));
|
|
1825
|
+
}
|
|
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
|
+
};
|
|
1852
|
+
const setupYubiKeySlot = async () => {
|
|
1853
|
+
setStep("yubikey-configuring");
|
|
1854
|
+
try {
|
|
1855
|
+
const { spawn: spawn3 } = await import('child_process');
|
|
1856
|
+
const args = ["otp", "chalresp", "--touch", "--generate", "2"];
|
|
1857
|
+
if (selectedYubiKeySerial) {
|
|
1858
|
+
args.unshift("--device", selectedYubiKeySerial);
|
|
1859
|
+
}
|
|
1860
|
+
await new Promise((resolve2, reject) => {
|
|
1861
|
+
const proc = spawn3("ykman", args, { stdio: "inherit" });
|
|
1862
|
+
proc.on("close", (code) => {
|
|
1863
|
+
if (code === 0) {
|
|
1864
|
+
resolve2();
|
|
1865
|
+
} else {
|
|
1866
|
+
reject(new Error(`ykman exited with code ${String(code)}`));
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
proc.on("error", reject);
|
|
1870
|
+
});
|
|
1871
|
+
setSelectedYubiKeySlot(2);
|
|
1872
|
+
void generateKeys("yubikey");
|
|
1873
|
+
} catch (err) {
|
|
1874
|
+
onError(err instanceof Error ? err : new Error("Failed to configure YubiKey"));
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1743
1877
|
const generateKeys = async (provider) => {
|
|
1744
1878
|
setStep("generating");
|
|
1745
1879
|
try {
|
|
1746
1880
|
const publicKeyPath = props.publicKeyPath ?? core.getDefaultPublicKeyPath();
|
|
1747
1881
|
if (provider === "filesystem") {
|
|
1748
1882
|
const fsProvider = new core.FilesystemKeyProvider();
|
|
1749
|
-
const genOptions = {
|
|
1883
|
+
const genOptions = {
|
|
1884
|
+
publicKeyPath
|
|
1885
|
+
};
|
|
1750
1886
|
if (props.force !== void 0) {
|
|
1751
1887
|
genOptions.force = props.force;
|
|
1752
1888
|
}
|
|
1889
|
+
if (encryptionPassphrase !== void 0) {
|
|
1890
|
+
genOptions.passphrase = encryptionPassphrase;
|
|
1891
|
+
}
|
|
1753
1892
|
const result = await fsProvider.generateKeyPair(genOptions);
|
|
1754
|
-
|
|
1893
|
+
const completionResult = {
|
|
1755
1894
|
provider: "filesystem",
|
|
1756
1895
|
publicKeyPath: result.publicKeyPath,
|
|
1757
1896
|
privateKeyRef: result.privateKeyRef,
|
|
1758
1897
|
storageDescription: result.storageDescription
|
|
1759
|
-
}
|
|
1898
|
+
};
|
|
1899
|
+
if (result.encrypted) {
|
|
1900
|
+
completionResult.encrypted = result.encrypted;
|
|
1901
|
+
}
|
|
1902
|
+
onComplete(completionResult);
|
|
1760
1903
|
} else if (provider === "1password") {
|
|
1761
1904
|
if (!selectedVault || !itemName) {
|
|
1762
1905
|
throw new Error("Vault and item name are required for 1Password");
|
|
@@ -1790,7 +1933,7 @@ function KeygenInteractive(props) {
|
|
|
1790
1933
|
completionResult.account = selectedAccount;
|
|
1791
1934
|
}
|
|
1792
1935
|
onComplete(completionResult);
|
|
1793
|
-
} else {
|
|
1936
|
+
} else if (provider === "macos-keychain") {
|
|
1794
1937
|
if (!keychainItemName) {
|
|
1795
1938
|
throw new Error("Item name is required for macOS Keychain");
|
|
1796
1939
|
}
|
|
@@ -1809,10 +1952,39 @@ function KeygenInteractive(props) {
|
|
|
1809
1952
|
storageDescription: result.storageDescription,
|
|
1810
1953
|
itemName: keychainItemName
|
|
1811
1954
|
});
|
|
1955
|
+
} else {
|
|
1956
|
+
const encryptedKeyPath = core.getDefaultYubiKeyEncryptedKeyPath();
|
|
1957
|
+
const providerOptions = {
|
|
1958
|
+
encryptedKeyPath,
|
|
1959
|
+
slot: selectedYubiKeySlot
|
|
1960
|
+
};
|
|
1961
|
+
if (selectedYubiKeySerial !== void 0) {
|
|
1962
|
+
providerOptions.serial = selectedYubiKeySerial;
|
|
1963
|
+
}
|
|
1964
|
+
const ykProvider = new core.YubiKeyProvider(providerOptions);
|
|
1965
|
+
const genOptions = { publicKeyPath };
|
|
1966
|
+
if (props.force !== void 0) {
|
|
1967
|
+
genOptions.force = props.force;
|
|
1968
|
+
}
|
|
1969
|
+
const result = await ykProvider.generateKeyPair(genOptions);
|
|
1970
|
+
const completionResult = {
|
|
1971
|
+
provider: "yubikey",
|
|
1972
|
+
publicKeyPath: result.publicKeyPath,
|
|
1973
|
+
privateKeyRef: result.privateKeyRef,
|
|
1974
|
+
storageDescription: result.storageDescription,
|
|
1975
|
+
slot: selectedYubiKeySlot,
|
|
1976
|
+
encryptedKeyPath
|
|
1977
|
+
};
|
|
1978
|
+
if (selectedYubiKeySerial !== void 0) {
|
|
1979
|
+
completionResult.serial = selectedYubiKeySerial;
|
|
1980
|
+
}
|
|
1981
|
+
onComplete(completionResult);
|
|
1812
1982
|
}
|
|
1813
1983
|
setStep("done");
|
|
1814
1984
|
} catch (err) {
|
|
1815
1985
|
onError(err instanceof Error ? err : new Error("Key generation failed"));
|
|
1986
|
+
} finally {
|
|
1987
|
+
setEncryptionPassphrase(void 0);
|
|
1816
1988
|
}
|
|
1817
1989
|
};
|
|
1818
1990
|
if (step === "checking-providers") {
|
|
@@ -1834,6 +2006,12 @@ function KeygenInteractive(props) {
|
|
|
1834
2006
|
value: "macos-keychain"
|
|
1835
2007
|
});
|
|
1836
2008
|
}
|
|
2009
|
+
if (yubiKeyAvailable) {
|
|
2010
|
+
options.push({
|
|
2011
|
+
label: "YubiKey (hardware security key)",
|
|
2012
|
+
value: "yubikey"
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
1837
2015
|
if (opAvailable) {
|
|
1838
2016
|
options.push({
|
|
1839
2017
|
label: "1Password (requires op CLI)",
|
|
@@ -1846,9 +2024,42 @@ function KeygenInteractive(props) {
|
|
|
1846
2024
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleProviderSelect })
|
|
1847
2025
|
] });
|
|
1848
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
|
+
}
|
|
1849
2060
|
if (step === "select-account") {
|
|
1850
2061
|
const options = accounts.map((account) => ({
|
|
1851
|
-
label: account.email,
|
|
2062
|
+
label: account.name ? `${account.name} (${account.email})` : account.email,
|
|
1852
2063
|
value: account.user_uuid
|
|
1853
2064
|
}));
|
|
1854
2065
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
@@ -1890,6 +2101,65 @@ function KeygenInteractive(props) {
|
|
|
1890
2101
|
/* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, { defaultValue: keychainItemName, onSubmit: handleKeychainItemNameSubmit })
|
|
1891
2102
|
] });
|
|
1892
2103
|
}
|
|
2104
|
+
if (step === "select-yubikey-device") {
|
|
2105
|
+
const options = yubiKeyDevices.map((device) => ({
|
|
2106
|
+
label: `${device.type} (Serial: ${device.serial})`,
|
|
2107
|
+
value: device.serial
|
|
2108
|
+
}));
|
|
2109
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2110
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Select YubiKey device:" }),
|
|
2111
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2112
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleYubiKeyDeviceSelect })
|
|
2113
|
+
] });
|
|
2114
|
+
}
|
|
2115
|
+
if (step === "select-yubikey-slot") {
|
|
2116
|
+
const options = [];
|
|
2117
|
+
if (slot2Configured) {
|
|
2118
|
+
options.push({
|
|
2119
|
+
label: "Slot 2 (recommended)",
|
|
2120
|
+
value: "2"
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
if (slot1Configured) {
|
|
2124
|
+
options.push({
|
|
2125
|
+
label: "Slot 1",
|
|
2126
|
+
value: "1"
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2130
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Select YubiKey slot for challenge-response:" }),
|
|
2131
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2132
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleYubiKeySlotSelect })
|
|
2133
|
+
] });
|
|
2134
|
+
}
|
|
2135
|
+
if (step === "yubikey-offer-setup") {
|
|
2136
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2137
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Your YubiKey is not configured for challenge-response." }),
|
|
2138
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2139
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "Would you like to configure slot 2 for challenge-response now?" }),
|
|
2140
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "This will enable touch-to-sign functionality." }),
|
|
2141
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2142
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2143
|
+
ui.Select,
|
|
2144
|
+
{
|
|
2145
|
+
options: [
|
|
2146
|
+
{ label: "Yes, configure my YubiKey", value: "yes" },
|
|
2147
|
+
{ label: "No, cancel", value: "no" }
|
|
2148
|
+
],
|
|
2149
|
+
onChange: handleYubiKeySetupConfirm
|
|
2150
|
+
}
|
|
2151
|
+
)
|
|
2152
|
+
] });
|
|
2153
|
+
}
|
|
2154
|
+
if (step === "yubikey-configuring") {
|
|
2155
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2156
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 1, children: [
|
|
2157
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}),
|
|
2158
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "Configuring YubiKey slot 2 for challenge-response..." })
|
|
2159
|
+
] }),
|
|
2160
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Touch your YubiKey when it flashes." })
|
|
2161
|
+
] });
|
|
2162
|
+
}
|
|
1893
2163
|
if (step === "generating") {
|
|
1894
2164
|
return /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 1, children: [
|
|
1895
2165
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}),
|
|
@@ -2202,7 +2472,7 @@ async function runVerify(gates, options) {
|
|
|
2202
2472
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2203
2473
|
}
|
|
2204
2474
|
const projectRoot = process.cwd();
|
|
2205
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
2475
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
2206
2476
|
const gatesToVerify = gates.length > 0 ? gates : Object.keys(attestItConfig.gates);
|
|
2207
2477
|
for (const gateId of gatesToVerify) {
|
|
2208
2478
|
if (!attestItConfig.gates[gateId]) {
|
|
@@ -2358,7 +2628,7 @@ async function runSeal(gates, options) {
|
|
|
2358
2628
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2359
2629
|
}
|
|
2360
2630
|
const projectRoot = process.cwd();
|
|
2361
|
-
const sealsFile = core.readSealsSync(projectRoot);
|
|
2631
|
+
const sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
2362
2632
|
const gatesToSeal = gates.length > 0 ? gates : getAllGateIds(attestItConfig);
|
|
2363
2633
|
for (const gateId of gatesToSeal) {
|
|
2364
2634
|
if (!attestItConfig.gates[gateId]) {
|
|
@@ -2371,9 +2641,17 @@ async function runSeal(gates, options) {
|
|
|
2371
2641
|
skipped: [],
|
|
2372
2642
|
failed: []
|
|
2373
2643
|
};
|
|
2644
|
+
const identitySlug = localConfig.activeIdentity;
|
|
2374
2645
|
for (const gateId of gatesToSeal) {
|
|
2375
2646
|
try {
|
|
2376
|
-
const result = await processSingleGate(
|
|
2647
|
+
const result = await processSingleGate(
|
|
2648
|
+
gateId,
|
|
2649
|
+
attestItConfig,
|
|
2650
|
+
identity,
|
|
2651
|
+
identitySlug,
|
|
2652
|
+
sealsFile,
|
|
2653
|
+
options
|
|
2654
|
+
);
|
|
2377
2655
|
if (result.sealed) {
|
|
2378
2656
|
summary.sealed.push(gateId);
|
|
2379
2657
|
} else if (result.skipped) {
|
|
@@ -2385,7 +2663,7 @@ async function runSeal(gates, options) {
|
|
|
2385
2663
|
}
|
|
2386
2664
|
}
|
|
2387
2665
|
if (!options.dryRun && summary.sealed.length > 0) {
|
|
2388
|
-
core.writeSealsSync(projectRoot, sealsFile);
|
|
2666
|
+
core.writeSealsSync(projectRoot, sealsFile, attestItConfig.settings.sealsPath);
|
|
2389
2667
|
}
|
|
2390
2668
|
displaySummary(summary, options.dryRun);
|
|
2391
2669
|
if (summary.failed.length > 0) {
|
|
@@ -2404,7 +2682,7 @@ async function runSeal(gates, options) {
|
|
|
2404
2682
|
process.exit(ExitCode.CONFIG_ERROR);
|
|
2405
2683
|
}
|
|
2406
2684
|
}
|
|
2407
|
-
async function processSingleGate(gateId, config, identity, sealsFile, options) {
|
|
2685
|
+
async function processSingleGate(gateId, config, identity, identitySlug, sealsFile, options) {
|
|
2408
2686
|
verbose(`Processing gate: ${gateId}`);
|
|
2409
2687
|
const gate = core.getGate(config, gateId);
|
|
2410
2688
|
if (!gate) {
|
|
@@ -2444,12 +2722,12 @@ async function processSingleGate(gateId, config, identity, sealsFile, options) {
|
|
|
2444
2722
|
const seal = core.createSeal({
|
|
2445
2723
|
gateId,
|
|
2446
2724
|
fingerprint: fingerprintResult.fingerprint,
|
|
2447
|
-
sealedBy:
|
|
2725
|
+
sealedBy: identitySlug,
|
|
2448
2726
|
privateKey: privateKeyPem
|
|
2449
2727
|
});
|
|
2450
2728
|
sealsFile.seals[gateId] = seal;
|
|
2451
2729
|
log(` Sealed gate: ${gateId}`);
|
|
2452
|
-
verbose(` Sealed by: ${identity.name}`);
|
|
2730
|
+
verbose(` Sealed by: ${identitySlug} (${identity.name})`);
|
|
2453
2731
|
verbose(` Timestamp: ${seal.timestamp}`);
|
|
2454
2732
|
return { sealed: true, skipped: false };
|
|
2455
2733
|
}
|
|
@@ -2974,6 +3252,7 @@ async function runCreate() {
|
|
|
2974
3252
|
};
|
|
2975
3253
|
}
|
|
2976
3254
|
await core.saveLocalConfig(newConfig);
|
|
3255
|
+
const publicKeyResult = await core.savePublicKey(slug, keyPair.publicKey);
|
|
2977
3256
|
log("");
|
|
2978
3257
|
success("Identity created successfully");
|
|
2979
3258
|
log("");
|
|
@@ -2988,6 +3267,12 @@ async function runCreate() {
|
|
|
2988
3267
|
log(` Public Key: ${keyPair.publicKey.slice(0, 32)}...`);
|
|
2989
3268
|
log(` Private Key: ${keyStorageDescription}`);
|
|
2990
3269
|
log("");
|
|
3270
|
+
log(theme3.blue.bold()("Public key saved to:"));
|
|
3271
|
+
log(` ${publicKeyResult.homePath}`);
|
|
3272
|
+
if (publicKeyResult.projectPath) {
|
|
3273
|
+
log(` ${publicKeyResult.projectPath}`);
|
|
3274
|
+
}
|
|
3275
|
+
log("");
|
|
2991
3276
|
if (!existingConfig) {
|
|
2992
3277
|
success(`Set as active identity`);
|
|
2993
3278
|
log("");
|
|
@@ -3879,7 +4164,7 @@ async function runRemove2(slug, options) {
|
|
|
3879
4164
|
const projectRoot = process.cwd();
|
|
3880
4165
|
let sealsFile;
|
|
3881
4166
|
try {
|
|
3882
|
-
sealsFile = core.readSealsSync(projectRoot);
|
|
4167
|
+
sealsFile = core.readSealsSync(projectRoot, attestItConfig.settings.sealsPath);
|
|
3883
4168
|
} catch {
|
|
3884
4169
|
sealsFile = { version: 1, seals: {} };
|
|
3885
4170
|
}
|