shiva-code 0.7.7 → 0.7.10
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/{chunk-RQ75X32M.js → chunk-4OT4OY7L.js} +175 -25
- package/dist/chunk-GGNOX6DN.js +195 -0
- package/dist/{chunk-KW3LP3K3.js → chunk-IWDZCN4S.js} +61 -35
- package/dist/{chunk-J5IS642F.js → chunk-KB6M24M3.js} +10 -0
- package/dist/chunk-UABU5VVI.js +246 -0
- package/dist/{client-RPSJP4Z5.js → client-H3JXPT5B.js} +1 -1
- package/dist/{hook-GIK5RFRC.js → hook-CCATCFG7.js} +2 -2
- package/dist/index.js +454 -247
- package/dist/{logger-E7SC5KUO.js → logger-VVWOD6AA.js} +5 -3
- package/dist/{login-Y6EJABSP.js → login-DBN2L25M.js} +3 -3
- package/dist/token-detection-K6KCIWAU.js +19 -0
- package/package.json +1 -1
- package/dist/chunk-Z6NXFC4Q.js +0 -118
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
colors,
|
|
2
3
|
log
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UABU5VVI.js";
|
|
4
5
|
import {
|
|
5
6
|
formatContextAsMarkdown,
|
|
6
7
|
generateGitHubContext,
|
|
@@ -1318,7 +1319,7 @@ function removeShivaHooks(eventHooks) {
|
|
|
1318
1319
|
);
|
|
1319
1320
|
}
|
|
1320
1321
|
var hookCommand = new Command("hook").description("Claude Code Hook Integration verwalten");
|
|
1321
|
-
hookCommand.command("install").description("SHIVA Hooks in Claude Code installieren").option("--github", "GitHub Context Injection aktivieren").option("--sync", "Cloud Sync Hooks aktivieren (Standard)").option("--scan", "Package Security Scanning aktivieren").option("--all", "Alle Hooks aktivieren").action((options) => {
|
|
1322
|
+
hookCommand.command("install").description("SHIVA Hooks in Claude Code installieren").option("--github", "GitHub Context Injection aktivieren").option("--sync", "Cloud Sync Hooks aktivieren (Standard)").option("--scan", "Package Security Scanning aktivieren").option("--token-protection", "Token-Schutz aktivieren (erkennt & sch\xFCtzt Secrets)").option("--all", "Alle Hooks aktivieren").action((options) => {
|
|
1322
1323
|
log.brand();
|
|
1323
1324
|
const claudePath = join(homedir(), ".claude");
|
|
1324
1325
|
if (!existsSync(claudePath)) {
|
|
@@ -1334,12 +1335,14 @@ hookCommand.command("install").description("SHIVA Hooks in Claude Code installie
|
|
|
1334
1335
|
if (!settings.hooks) {
|
|
1335
1336
|
settings.hooks = {};
|
|
1336
1337
|
}
|
|
1337
|
-
const installSync = options.sync || options.all || !options.github && !options.scan;
|
|
1338
|
+
const installSync = options.sync || options.all || !options.github && !options.scan && !options.tokenProtection;
|
|
1338
1339
|
const installGithub = options.github || options.all;
|
|
1339
1340
|
const installScan = options.scan || options.all;
|
|
1341
|
+
const installTokenProtection = options.tokenProtection || options.all;
|
|
1340
1342
|
const hasSyncHooks = hasShivaHook(settings.hooks, "SessionStart") || hasShivaHook(settings.hooks, "Stop");
|
|
1341
1343
|
const hasGithubHook = hasShivaContextHook(settings.hooks);
|
|
1342
1344
|
const hasScanHook = hasShivaScanHook(settings.hooks);
|
|
1345
|
+
const hasTokenHook = hasShivaTokenHook(settings.hooks);
|
|
1343
1346
|
if (hasSyncHooks && installSync && !installGithub && !installScan) {
|
|
1344
1347
|
log.warn("SHIVA Sync Hooks sind bereits installiert");
|
|
1345
1348
|
log.newline();
|
|
@@ -1412,6 +1415,21 @@ hookCommand.command("install").description("SHIVA Hooks in Claude Code installie
|
|
|
1412
1415
|
});
|
|
1413
1416
|
installedSomething = true;
|
|
1414
1417
|
}
|
|
1418
|
+
if (installTokenProtection && !hasTokenHook) {
|
|
1419
|
+
if (!settings.hooks.Stop) {
|
|
1420
|
+
settings.hooks.Stop = [];
|
|
1421
|
+
}
|
|
1422
|
+
settings.hooks.Stop.push({
|
|
1423
|
+
hooks: [
|
|
1424
|
+
{
|
|
1425
|
+
type: "command",
|
|
1426
|
+
command: "shiva hook scan-session --quiet",
|
|
1427
|
+
timeout: 30
|
|
1428
|
+
}
|
|
1429
|
+
]
|
|
1430
|
+
});
|
|
1431
|
+
installedSomething = true;
|
|
1432
|
+
}
|
|
1415
1433
|
if (!installedSomething) {
|
|
1416
1434
|
log.warn("Alle ausgew\xE4hlten Hooks sind bereits installiert");
|
|
1417
1435
|
log.newline();
|
|
@@ -1432,6 +1450,9 @@ hookCommand.command("install").description("SHIVA Hooks in Claude Code installie
|
|
|
1432
1450
|
if (installScan || hasScanHook) {
|
|
1433
1451
|
log.tree.item("PreToolUse: Package Security Scanning");
|
|
1434
1452
|
}
|
|
1453
|
+
if (installTokenProtection || hasTokenHook) {
|
|
1454
|
+
log.tree.item("Stop: Token-Schutz (Secrets erkennen & sch\xFCtzen)");
|
|
1455
|
+
}
|
|
1435
1456
|
log.newline();
|
|
1436
1457
|
log.dim("Claude Code wird nun automatisch mit SHIVA integriert.");
|
|
1437
1458
|
log.newline();
|
|
@@ -1452,6 +1473,13 @@ function hasShivaScanHook(hooks) {
|
|
|
1452
1473
|
(entry) => entry.matcher === "Bash" && entry.hooks?.some((h) => h.command?.includes("shiva hook scan-command"))
|
|
1453
1474
|
);
|
|
1454
1475
|
}
|
|
1476
|
+
function hasShivaTokenHook(hooks) {
|
|
1477
|
+
if (!hooks || !hooks.Stop) return false;
|
|
1478
|
+
const eventHooks = hooks.Stop;
|
|
1479
|
+
return eventHooks.some(
|
|
1480
|
+
(entry) => entry.hooks?.some((h) => h.command?.includes("shiva hook scan-session"))
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1455
1483
|
hookCommand.command("uninstall").description("SHIVA Hooks aus Claude Code entfernen").action(() => {
|
|
1456
1484
|
log.brand();
|
|
1457
1485
|
const settings = getClaudeSettings();
|
|
@@ -1503,6 +1531,7 @@ hookCommand.command("status").description("Hook-Status anzeigen").action(() => {
|
|
|
1503
1531
|
const hasStop = hasShivaHook(settings.hooks, "Stop");
|
|
1504
1532
|
const hasGithub = hasShivaContextHook(settings.hooks);
|
|
1505
1533
|
const hasScan = hasShivaScanHook(settings.hooks);
|
|
1534
|
+
const hasToken = hasShivaTokenHook(settings.hooks);
|
|
1506
1535
|
log.plain("Cloud Sync:");
|
|
1507
1536
|
if (hasSessionStart) {
|
|
1508
1537
|
log.listItem("SessionStart: Memories laden", "synced");
|
|
@@ -1528,8 +1557,13 @@ hookCommand.command("status").description("Hook-Status anzeigen").action(() => {
|
|
|
1528
1557
|
} else {
|
|
1529
1558
|
log.listItem("Package Scanning: nicht aktiv", "pending");
|
|
1530
1559
|
}
|
|
1560
|
+
if (hasToken) {
|
|
1561
|
+
log.listItem("Stop: Token-Schutz (Secrets erkennen)", "synced");
|
|
1562
|
+
} else {
|
|
1563
|
+
log.listItem("Token-Schutz: nicht aktiv", "pending");
|
|
1564
|
+
}
|
|
1531
1565
|
log.newline();
|
|
1532
|
-
const allInstalled = hasSessionStart && hasStop && hasGithub && hasScan;
|
|
1566
|
+
const allInstalled = hasSessionStart && hasStop && hasGithub && hasScan && hasToken;
|
|
1533
1567
|
const syncInstalled = hasSessionStart && hasStop;
|
|
1534
1568
|
const anyInstalled = hasSessionStart || hasStop || hasGithub || hasScan;
|
|
1535
1569
|
if (allInstalled) {
|
|
@@ -1659,7 +1693,7 @@ hookCommand.command("scan-command").description("Scanne Bash-Befehle auf Package
|
|
|
1659
1693
|
}
|
|
1660
1694
|
});
|
|
1661
1695
|
hookCommand.command("push").description("Hooks in Cloud sichern").action(async () => {
|
|
1662
|
-
const { api } = await import("./client-
|
|
1696
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1663
1697
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1664
1698
|
if (!isAuthenticated()) {
|
|
1665
1699
|
log.error("Nicht angemeldet");
|
|
@@ -1679,7 +1713,7 @@ hookCommand.command("push").description("Hooks in Cloud sichern").action(async (
|
|
|
1679
1713
|
}
|
|
1680
1714
|
});
|
|
1681
1715
|
hookCommand.command("pull").description("Hooks aus Cloud laden").option("-f, --force", "Lokale Hooks \xFCberschreiben").action(async (options) => {
|
|
1682
|
-
const { api } = await import("./client-
|
|
1716
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1683
1717
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1684
1718
|
if (!isAuthenticated()) {
|
|
1685
1719
|
log.error("Nicht angemeldet");
|
|
@@ -1711,7 +1745,7 @@ hookCommand.command("pull").description("Hooks aus Cloud laden").option("-f, --f
|
|
|
1711
1745
|
}
|
|
1712
1746
|
});
|
|
1713
1747
|
hookCommand.command("sync").description("Hooks mit Cloud synchronisieren").action(async () => {
|
|
1714
|
-
const { api } = await import("./client-
|
|
1748
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1715
1749
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1716
1750
|
if (!isAuthenticated()) {
|
|
1717
1751
|
log.error("Nicht angemeldet");
|
|
@@ -1738,9 +1772,9 @@ hookCommand.command("sync").description("Hooks mit Cloud synchronisieren").actio
|
|
|
1738
1772
|
}
|
|
1739
1773
|
});
|
|
1740
1774
|
hookCommand.command("cloud-list").description("Cloud-Hooks auflisten").option("--event <type>", "Nach Event-Typ filtern").option("--json", "JSON Output").action(async (options) => {
|
|
1741
|
-
const { api } = await import("./client-
|
|
1775
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1742
1776
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1743
|
-
const { colors } = await import("./logger-
|
|
1777
|
+
const { colors: colors2 } = await import("./logger-VVWOD6AA.js");
|
|
1744
1778
|
if (!isAuthenticated()) {
|
|
1745
1779
|
log.error("Nicht angemeldet");
|
|
1746
1780
|
log.info("Anmelden mit: shiva login");
|
|
@@ -1762,8 +1796,8 @@ hookCommand.command("cloud-list").description("Cloud-Hooks auflisten").option("-
|
|
|
1762
1796
|
return;
|
|
1763
1797
|
}
|
|
1764
1798
|
log.newline();
|
|
1765
|
-
console.log(
|
|
1766
|
-
console.log(
|
|
1799
|
+
console.log(colors2.orange.bold("Cloud Hooks"));
|
|
1800
|
+
console.log(colors2.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1767
1801
|
log.newline();
|
|
1768
1802
|
if (!hooks || Array.isArray(hooks) && hooks.length === 0) {
|
|
1769
1803
|
log.dim("Keine Cloud-Hooks konfiguriert");
|
|
@@ -1771,14 +1805,14 @@ hookCommand.command("cloud-list").description("Cloud-Hooks auflisten").option("-
|
|
|
1771
1805
|
}
|
|
1772
1806
|
const hooksArray = Array.isArray(hooks) ? hooks : Object.values(hooks).flat();
|
|
1773
1807
|
for (const hook of hooksArray) {
|
|
1774
|
-
const enabledIcon = hook.enabled !== false ?
|
|
1775
|
-
console.log(` ${enabledIcon} ${
|
|
1776
|
-
console.log(` ${
|
|
1808
|
+
const enabledIcon = hook.enabled !== false ? colors2.green("\u25CF") : colors2.dim("\u25CB");
|
|
1809
|
+
console.log(` ${enabledIcon} ${colors2.bold(hook.id || hook.event)}`);
|
|
1810
|
+
console.log(` ${colors2.dim("Event:")} ${hook.event}`);
|
|
1777
1811
|
if (hook.matcher) {
|
|
1778
|
-
console.log(` ${
|
|
1812
|
+
console.log(` ${colors2.dim("Matcher:")} ${hook.matcher}`);
|
|
1779
1813
|
}
|
|
1780
|
-
console.log(` ${
|
|
1781
|
-
console.log(` ${
|
|
1814
|
+
console.log(` ${colors2.dim("Type:")} ${hook.type}`);
|
|
1815
|
+
console.log(` ${colors2.dim("Command:")} ${hook.command}`);
|
|
1782
1816
|
log.newline();
|
|
1783
1817
|
}
|
|
1784
1818
|
} catch (error) {
|
|
@@ -1787,7 +1821,7 @@ hookCommand.command("cloud-list").description("Cloud-Hooks auflisten").option("-
|
|
|
1787
1821
|
}
|
|
1788
1822
|
});
|
|
1789
1823
|
hookCommand.command("cloud-create").description("Neuen Cloud-Hook erstellen").requiredOption("--event <event>", "Event-Typ (PreToolUse, PostToolUse, etc.)").requiredOption("--command <cmd>", "Auszuf\xFChrender Befehl").option("--matcher <pattern>", "Tool-Matcher Pattern").option("--type <type>", "Hook-Typ (command, script)", "command").option("--timeout <ms>", "Timeout in Millisekunden").action(async (options) => {
|
|
1790
|
-
const { api } = await import("./client-
|
|
1824
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1791
1825
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1792
1826
|
if (!isAuthenticated()) {
|
|
1793
1827
|
log.error("Nicht angemeldet");
|
|
@@ -1815,9 +1849,9 @@ hookCommand.command("cloud-create").description("Neuen Cloud-Hook erstellen").re
|
|
|
1815
1849
|
}
|
|
1816
1850
|
});
|
|
1817
1851
|
hookCommand.command("cloud-test").description("Cloud-Hook testen").argument("<hook-id>", "Hook ID").action(async (hookId) => {
|
|
1818
|
-
const { api } = await import("./client-
|
|
1852
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1819
1853
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1820
|
-
const { colors } = await import("./logger-
|
|
1854
|
+
const { colors: colors2 } = await import("./logger-VVWOD6AA.js");
|
|
1821
1855
|
if (!isAuthenticated()) {
|
|
1822
1856
|
log.error("Nicht angemeldet");
|
|
1823
1857
|
log.info("Anmelden mit: shiva login");
|
|
@@ -1833,15 +1867,15 @@ hookCommand.command("cloud-test").description("Cloud-Hook testen").argument("<ho
|
|
|
1833
1867
|
log.success(`Hook erfolgreich (${result.duration}ms)`);
|
|
1834
1868
|
if (result.output) {
|
|
1835
1869
|
log.newline();
|
|
1836
|
-
console.log(
|
|
1870
|
+
console.log(colors2.dim("Output:"));
|
|
1837
1871
|
console.log(result.output);
|
|
1838
1872
|
}
|
|
1839
1873
|
} else {
|
|
1840
1874
|
log.error(`Hook fehlgeschlagen (${result.duration}ms)`);
|
|
1841
1875
|
if (result.error) {
|
|
1842
1876
|
log.newline();
|
|
1843
|
-
console.log(
|
|
1844
|
-
console.log(
|
|
1877
|
+
console.log(colors2.dim("Error:"));
|
|
1878
|
+
console.log(colors2.red(result.error));
|
|
1845
1879
|
}
|
|
1846
1880
|
}
|
|
1847
1881
|
} catch (error) {
|
|
@@ -1850,7 +1884,7 @@ hookCommand.command("cloud-test").description("Cloud-Hook testen").argument("<ho
|
|
|
1850
1884
|
}
|
|
1851
1885
|
});
|
|
1852
1886
|
hookCommand.command("cloud-delete").description("Cloud-Hook l\xF6schen").argument("<hook-id>", "Hook ID").option("-y, --yes", "Ohne Best\xE4tigung").action(async (hookId, options) => {
|
|
1853
|
-
const { api } = await import("./client-
|
|
1887
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1854
1888
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1855
1889
|
if (!isAuthenticated()) {
|
|
1856
1890
|
log.error("Nicht angemeldet");
|
|
@@ -1885,7 +1919,7 @@ hookCommand.command("cloud-delete").description("Cloud-Hook l\xF6schen").argumen
|
|
|
1885
1919
|
}
|
|
1886
1920
|
});
|
|
1887
1921
|
hookCommand.command("cloud-toggle").description("Cloud-Hook aktivieren/deaktivieren").argument("<hook-id>", "Hook ID").option("--enable", "Hook aktivieren").option("--disable", "Hook deaktivieren").action(async (hookId, options) => {
|
|
1888
|
-
const { api } = await import("./client-
|
|
1922
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1889
1923
|
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1890
1924
|
if (!isAuthenticated()) {
|
|
1891
1925
|
log.error("Nicht angemeldet");
|
|
@@ -1911,6 +1945,122 @@ hookCommand.command("cloud-toggle").description("Cloud-Hook aktivieren/deaktivie
|
|
|
1911
1945
|
log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
|
|
1912
1946
|
}
|
|
1913
1947
|
});
|
|
1948
|
+
hookCommand.command("scan-session").description("Session nach sensiblen Tokens scannen (f\xFCr Stop Hook)").option("--quiet", "Keine Ausgabe (f\xFCr Hook)").option("--redact", "Tokens in Session-History maskieren").option("--path <path>", "Session-Datei Pfad").action(async (options) => {
|
|
1949
|
+
const { detectTokens, redactTokens, maskToken } = await import("./token-detection-K6KCIWAU.js");
|
|
1950
|
+
const { api } = await import("./client-H3JXPT5B.js");
|
|
1951
|
+
const { isAuthenticated } = await import("./config-FGMZONWV.js");
|
|
1952
|
+
const { existsSync: existsSync2, readFileSync: readFileSync2, writeFileSync: writeFileSync2, readdirSync, statSync } = await import("fs");
|
|
1953
|
+
const { join: join2 } = await import("path");
|
|
1954
|
+
const { homedir: homedir2 } = await import("os");
|
|
1955
|
+
const quiet = options.quiet;
|
|
1956
|
+
try {
|
|
1957
|
+
let autoRedact = options.redact || false;
|
|
1958
|
+
let autoStore = false;
|
|
1959
|
+
if (isAuthenticated()) {
|
|
1960
|
+
try {
|
|
1961
|
+
const settings = await api.getUserSettings();
|
|
1962
|
+
if (settings.settings?.tokenProtection) {
|
|
1963
|
+
autoRedact = settings.settings.tokenProtection.autoRedact ?? autoRedact;
|
|
1964
|
+
autoStore = settings.settings.tokenProtection.autoStore ?? false;
|
|
1965
|
+
}
|
|
1966
|
+
} catch {
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
const claudeProjectsPath = join2(homedir2(), ".claude", "projects");
|
|
1970
|
+
let sessionFiles = [];
|
|
1971
|
+
if (options.path) {
|
|
1972
|
+
sessionFiles = [options.path];
|
|
1973
|
+
} else if (existsSync2(claudeProjectsPath)) {
|
|
1974
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1e3;
|
|
1975
|
+
try {
|
|
1976
|
+
const projects = readdirSync(claudeProjectsPath);
|
|
1977
|
+
for (const project of projects) {
|
|
1978
|
+
const projectPath = join2(claudeProjectsPath, project);
|
|
1979
|
+
const stat = statSync(projectPath);
|
|
1980
|
+
if (!stat.isDirectory()) continue;
|
|
1981
|
+
const files = readdirSync(projectPath);
|
|
1982
|
+
for (const file of files) {
|
|
1983
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
1984
|
+
const filePath = join2(projectPath, file);
|
|
1985
|
+
const fileStat = statSync(filePath);
|
|
1986
|
+
if (fileStat.mtime.getTime() > oneHourAgo) {
|
|
1987
|
+
sessionFiles.push(filePath);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
} catch {
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
let totalTokensFound = 0;
|
|
1995
|
+
let totalFilesScanned = 0;
|
|
1996
|
+
let totalTokensRedacted = 0;
|
|
1997
|
+
const detectedTokens = [];
|
|
1998
|
+
for (const sessionFile of sessionFiles) {
|
|
1999
|
+
if (!existsSync2(sessionFile)) continue;
|
|
2000
|
+
try {
|
|
2001
|
+
const content = readFileSync2(sessionFile, "utf-8");
|
|
2002
|
+
const tokens = detectTokens(content);
|
|
2003
|
+
if (tokens.length > 0) {
|
|
2004
|
+
totalTokensFound += tokens.length;
|
|
2005
|
+
for (const t of tokens) {
|
|
2006
|
+
detectedTokens.push({
|
|
2007
|
+
file: sessionFile,
|
|
2008
|
+
token: t.token,
|
|
2009
|
+
service: t.pattern.service
|
|
2010
|
+
});
|
|
2011
|
+
if (autoStore && isAuthenticated()) {
|
|
2012
|
+
try {
|
|
2013
|
+
const secretKey = `${t.pattern.name.toUpperCase()}_TOKEN`;
|
|
2014
|
+
await api.addSecret({
|
|
2015
|
+
key: secretKey,
|
|
2016
|
+
value: t.token,
|
|
2017
|
+
description: `Auto-detected ${t.pattern.description}`
|
|
2018
|
+
});
|
|
2019
|
+
} catch {
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
if (autoRedact) {
|
|
2024
|
+
const redactedContent = redactTokens(content);
|
|
2025
|
+
writeFileSync2(sessionFile, redactedContent);
|
|
2026
|
+
totalTokensRedacted += tokens.length;
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
totalFilesScanned++;
|
|
2030
|
+
} catch {
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (quiet) {
|
|
2034
|
+
process.exit(0);
|
|
2035
|
+
} else {
|
|
2036
|
+
log.newline();
|
|
2037
|
+
if (totalTokensFound === 0) {
|
|
2038
|
+
log.success(`Keine sensiblen Tokens gefunden (${totalFilesScanned} Dateien gescannt)`);
|
|
2039
|
+
} else {
|
|
2040
|
+
console.log(colors.red.bold(`\u26A0\uFE0F ${totalTokensFound} sensible Tokens gefunden!`));
|
|
2041
|
+
log.newline();
|
|
2042
|
+
for (const t of detectedTokens) {
|
|
2043
|
+
console.log(` ${colors.red("!")} ${t.service}: ${colors.dim(maskToken(t.token))}`);
|
|
2044
|
+
}
|
|
2045
|
+
log.newline();
|
|
2046
|
+
if (totalTokensRedacted > 0) {
|
|
2047
|
+
log.success(`${totalTokensRedacted} Tokens in Session-History maskiert`);
|
|
2048
|
+
} else {
|
|
2049
|
+
log.warn("Tokens NICHT maskiert. Verwende --redact oder aktiviere auto-redact in Einstellungen.");
|
|
2050
|
+
}
|
|
2051
|
+
log.newline();
|
|
2052
|
+
log.info("Sichere Tokens mit: shiva secure-token");
|
|
2053
|
+
}
|
|
2054
|
+
log.newline();
|
|
2055
|
+
}
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
if (quiet) {
|
|
2058
|
+
process.exit(0);
|
|
2059
|
+
} else {
|
|
2060
|
+
log.error(error instanceof Error ? error.message : "Fehler beim Scannen");
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
1914
2064
|
|
|
1915
2065
|
export {
|
|
1916
2066
|
packageScanner,
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// src/utils/token-detection.ts
|
|
2
|
+
var TOKEN_PATTERNS = [
|
|
3
|
+
// NPM
|
|
4
|
+
{
|
|
5
|
+
name: "npm",
|
|
6
|
+
service: "NPM Registry",
|
|
7
|
+
pattern: /npm_[A-Za-z0-9]{36}/g,
|
|
8
|
+
configCommand: (token) => ["npm", "config", "set", "//registry.npmjs.org/:_authToken", token],
|
|
9
|
+
description: "NPM Access Token"
|
|
10
|
+
},
|
|
11
|
+
// GitHub
|
|
12
|
+
{
|
|
13
|
+
name: "github",
|
|
14
|
+
service: "GitHub",
|
|
15
|
+
pattern: /ghp_[A-Za-z0-9]{36}/g,
|
|
16
|
+
description: "GitHub Personal Access Token"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "github_pat",
|
|
20
|
+
service: "GitHub",
|
|
21
|
+
pattern: /github_pat_[A-Za-z0-9_]{80,}/g,
|
|
22
|
+
description: "GitHub Fine-grained Personal Access Token"
|
|
23
|
+
},
|
|
24
|
+
// OpenAI
|
|
25
|
+
{
|
|
26
|
+
name: "openai",
|
|
27
|
+
service: "OpenAI",
|
|
28
|
+
pattern: /sk-[A-Za-z0-9]{48}/g,
|
|
29
|
+
description: "OpenAI API Key"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "openai_proj",
|
|
33
|
+
service: "OpenAI",
|
|
34
|
+
pattern: /sk-proj-[A-Za-z0-9_-]{80,}/g,
|
|
35
|
+
description: "OpenAI Project API Key"
|
|
36
|
+
},
|
|
37
|
+
// Anthropic
|
|
38
|
+
{
|
|
39
|
+
name: "anthropic",
|
|
40
|
+
service: "Anthropic",
|
|
41
|
+
pattern: /sk-ant-[A-Za-z0-9_-]{80,}/g,
|
|
42
|
+
description: "Anthropic API Key"
|
|
43
|
+
},
|
|
44
|
+
// AWS
|
|
45
|
+
{
|
|
46
|
+
name: "aws_access_key",
|
|
47
|
+
service: "AWS",
|
|
48
|
+
pattern: /AKIA[A-Z0-9]{16}/g,
|
|
49
|
+
description: "AWS Access Key ID"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "aws_secret",
|
|
53
|
+
service: "AWS",
|
|
54
|
+
pattern: /(?<![A-Za-z0-9\/+])[A-Za-z0-9\/+=]{40}(?![A-Za-z0-9\/+=])/g,
|
|
55
|
+
description: "AWS Secret Access Key (potential)"
|
|
56
|
+
},
|
|
57
|
+
// Google Cloud
|
|
58
|
+
{
|
|
59
|
+
name: "gcp",
|
|
60
|
+
service: "Google Cloud",
|
|
61
|
+
pattern: /AIza[A-Za-z0-9_-]{35}/g,
|
|
62
|
+
description: "Google Cloud API Key"
|
|
63
|
+
},
|
|
64
|
+
// Stripe
|
|
65
|
+
{
|
|
66
|
+
name: "stripe_secret",
|
|
67
|
+
service: "Stripe",
|
|
68
|
+
pattern: /sk_live_[A-Za-z0-9]{24,}/g,
|
|
69
|
+
description: "Stripe Secret Key (Live)"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "stripe_test",
|
|
73
|
+
service: "Stripe",
|
|
74
|
+
pattern: /sk_test_[A-Za-z0-9]{24,}/g,
|
|
75
|
+
description: "Stripe Secret Key (Test)"
|
|
76
|
+
},
|
|
77
|
+
// Slack
|
|
78
|
+
{
|
|
79
|
+
name: "slack_bot",
|
|
80
|
+
service: "Slack",
|
|
81
|
+
pattern: /xoxb-[A-Za-z0-9-]{50,}/g,
|
|
82
|
+
description: "Slack Bot Token"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "slack_user",
|
|
86
|
+
service: "Slack",
|
|
87
|
+
pattern: /xoxp-[A-Za-z0-9-]{50,}/g,
|
|
88
|
+
description: "Slack User Token"
|
|
89
|
+
},
|
|
90
|
+
// Discord
|
|
91
|
+
{
|
|
92
|
+
name: "discord",
|
|
93
|
+
service: "Discord",
|
|
94
|
+
pattern: /[MN][A-Za-z0-9]{23,}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}/g,
|
|
95
|
+
description: "Discord Bot Token"
|
|
96
|
+
},
|
|
97
|
+
// Twilio
|
|
98
|
+
{
|
|
99
|
+
name: "twilio",
|
|
100
|
+
service: "Twilio",
|
|
101
|
+
pattern: /SK[a-f0-9]{32}/g,
|
|
102
|
+
description: "Twilio API Key"
|
|
103
|
+
},
|
|
104
|
+
// SendGrid
|
|
105
|
+
{
|
|
106
|
+
name: "sendgrid",
|
|
107
|
+
service: "SendGrid",
|
|
108
|
+
pattern: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
|
|
109
|
+
description: "SendGrid API Key"
|
|
110
|
+
},
|
|
111
|
+
// Mailchimp
|
|
112
|
+
{
|
|
113
|
+
name: "mailchimp",
|
|
114
|
+
service: "Mailchimp",
|
|
115
|
+
pattern: /[a-f0-9]{32}-us[0-9]{1,2}/g,
|
|
116
|
+
description: "Mailchimp API Key"
|
|
117
|
+
},
|
|
118
|
+
// Heroku
|
|
119
|
+
{
|
|
120
|
+
name: "heroku",
|
|
121
|
+
service: "Heroku",
|
|
122
|
+
pattern: /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/g,
|
|
123
|
+
description: "Heroku API Key (UUID format)"
|
|
124
|
+
},
|
|
125
|
+
// Generic JWT (potential sensitive)
|
|
126
|
+
{
|
|
127
|
+
name: "jwt",
|
|
128
|
+
service: "JWT",
|
|
129
|
+
pattern: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
|
|
130
|
+
description: "JSON Web Token"
|
|
131
|
+
},
|
|
132
|
+
// Private Keys
|
|
133
|
+
{
|
|
134
|
+
name: "private_key",
|
|
135
|
+
service: "Private Key",
|
|
136
|
+
pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g,
|
|
137
|
+
description: "Private Key Header"
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
function detectTokens(text) {
|
|
141
|
+
const detected = [];
|
|
142
|
+
for (const pattern of TOKEN_PATTERNS) {
|
|
143
|
+
pattern.pattern.lastIndex = 0;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = pattern.pattern.exec(text)) !== null) {
|
|
146
|
+
detected.push({
|
|
147
|
+
token: match[0],
|
|
148
|
+
pattern,
|
|
149
|
+
startIndex: match.index,
|
|
150
|
+
endIndex: match.index + match[0].length
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return detected.sort((a, b) => a.startIndex - b.startIndex).filter((item, index, array) => {
|
|
155
|
+
if (index === 0) return true;
|
|
156
|
+
const prev = array[index - 1];
|
|
157
|
+
return item.startIndex >= prev.endIndex;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function containsTokens(text) {
|
|
161
|
+
return detectTokens(text).length > 0;
|
|
162
|
+
}
|
|
163
|
+
function redactTokens(text, replacement = "****REDACTED****") {
|
|
164
|
+
const tokens = detectTokens(text);
|
|
165
|
+
if (tokens.length === 0) return text;
|
|
166
|
+
let result = text;
|
|
167
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
168
|
+
const { startIndex, endIndex, pattern } = tokens[i];
|
|
169
|
+
const redacted = `[${pattern.service}:${replacement}]`;
|
|
170
|
+
result = result.slice(0, startIndex) + redacted + result.slice(endIndex);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
function maskToken(token) {
|
|
175
|
+
if (token.length <= 12) {
|
|
176
|
+
return "****";
|
|
177
|
+
}
|
|
178
|
+
return `${token.slice(0, 6)}...${token.slice(-4)}`;
|
|
179
|
+
}
|
|
180
|
+
function getPatternByName(name) {
|
|
181
|
+
return TOKEN_PATTERNS.find((p) => p.name === name);
|
|
182
|
+
}
|
|
183
|
+
function getSupportedServices() {
|
|
184
|
+
return [...new Set(TOKEN_PATTERNS.map((p) => p.name))];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export {
|
|
188
|
+
TOKEN_PATTERNS,
|
|
189
|
+
detectTokens,
|
|
190
|
+
containsTokens,
|
|
191
|
+
redactTokens,
|
|
192
|
+
maskToken,
|
|
193
|
+
getPatternByName,
|
|
194
|
+
getSupportedServices
|
|
195
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
colors,
|
|
3
3
|
log
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UABU5VVI.js";
|
|
5
5
|
import {
|
|
6
6
|
api
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KB6M24M3.js";
|
|
8
8
|
import {
|
|
9
9
|
clearAuth,
|
|
10
10
|
getConfig,
|
|
@@ -414,44 +414,18 @@ async function loginWithOtp(email) {
|
|
|
414
414
|
}
|
|
415
415
|
]);
|
|
416
416
|
const authResponse = await api.verifyOtp(otpResponse.token, otp);
|
|
417
|
+
if (authResponse.requires2FA && authResponse.tempToken) {
|
|
418
|
+
log.newline();
|
|
419
|
+
log.info("2FA ist aktiviert. Bitte verifiziere dich.");
|
|
420
|
+
log.newline();
|
|
421
|
+
const result = await handle2FALoginFlow(authResponse.tempToken, authResponse.email || email);
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
417
424
|
if (!authResponse.success || !authResponse.token || !authResponse.user) {
|
|
418
425
|
log.error(authResponse.message || "Ung\xFCltiger Code");
|
|
419
426
|
return false;
|
|
420
427
|
}
|
|
421
428
|
setAuth(authResponse.token, authResponse.user);
|
|
422
|
-
try {
|
|
423
|
-
const tfaStatus = await twoFactorService.getStatus();
|
|
424
|
-
if (tfaStatus.enabled) {
|
|
425
|
-
log.newline();
|
|
426
|
-
log.info("2FA ist aktiviert. Bitte verifiziere dich.");
|
|
427
|
-
log.newline();
|
|
428
|
-
const isDeviceTrusted = await deviceTrustService.isDeviceTrusted();
|
|
429
|
-
if (!isDeviceTrusted) {
|
|
430
|
-
const verified = await prompt2FAVerification();
|
|
431
|
-
if (!verified) {
|
|
432
|
-
clearAuth();
|
|
433
|
-
log.error("2FA-Verifizierung fehlgeschlagen");
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
436
|
-
const { trustDevice } = await inquirer.prompt([{
|
|
437
|
-
type: "confirm",
|
|
438
|
-
name: "trustDevice",
|
|
439
|
-
message: "Diesem Ger\xE4t vertrauen? (Kein 2FA bei zuk\xFCnftigen Logins)",
|
|
440
|
-
default: true
|
|
441
|
-
}]);
|
|
442
|
-
if (trustDevice) {
|
|
443
|
-
try {
|
|
444
|
-
await deviceTrustService.trustDevice();
|
|
445
|
-
log.success("Ger\xE4t als vertrauensw\xFCrdig markiert");
|
|
446
|
-
} catch {
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
} else {
|
|
450
|
-
log.dim("Ger\xE4t ist vertrauensw\xFCrdig - 2FA \xFCbersprungen");
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
} catch {
|
|
454
|
-
}
|
|
455
429
|
log.newline();
|
|
456
430
|
log.success(`Angemeldet als ${authResponse.user.email} (${authResponse.user.tier.toUpperCase()})`);
|
|
457
431
|
return true;
|
|
@@ -461,6 +435,58 @@ async function loginWithOtp(email) {
|
|
|
461
435
|
return false;
|
|
462
436
|
}
|
|
463
437
|
}
|
|
438
|
+
async function handle2FALoginFlow(tempToken, email) {
|
|
439
|
+
const MAX_ATTEMPTS = 3;
|
|
440
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
441
|
+
const { code } = await inquirer.prompt([{
|
|
442
|
+
type: "input",
|
|
443
|
+
name: "code",
|
|
444
|
+
message: "2FA-Code (6 Ziffern oder Backup-Code):",
|
|
445
|
+
validate: (input) => {
|
|
446
|
+
if (/^\d{6}$/.test(input) || /^[A-Za-z0-9-]+$/.test(input)) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
return "Bitte gib einen g\xFCltigen Code ein";
|
|
450
|
+
}
|
|
451
|
+
}]);
|
|
452
|
+
const { rememberDevice } = await inquirer.prompt([{
|
|
453
|
+
type: "confirm",
|
|
454
|
+
name: "rememberDevice",
|
|
455
|
+
message: "Diesem Ger\xE4t vertrauen?",
|
|
456
|
+
default: true
|
|
457
|
+
}]);
|
|
458
|
+
try {
|
|
459
|
+
const result = await api.validate2FALogin({
|
|
460
|
+
tempToken,
|
|
461
|
+
code,
|
|
462
|
+
rememberDevice
|
|
463
|
+
});
|
|
464
|
+
if (result.success && result.token && result.user) {
|
|
465
|
+
setAuth(result.token, result.user);
|
|
466
|
+
if (result.method === "backup" && result.backupCodesRemaining !== void 0) {
|
|
467
|
+
log.warn(`Backup-Code verwendet. Noch ${result.backupCodesRemaining} Codes \xFCbrig.`);
|
|
468
|
+
}
|
|
469
|
+
log.newline();
|
|
470
|
+
log.success(`Angemeldet als ${result.user.email} (${result.user.tier.toUpperCase()})`);
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
474
|
+
log.error(`Ung\xFCltiger Code. Noch ${MAX_ATTEMPTS - attempt} Versuche.`);
|
|
475
|
+
}
|
|
476
|
+
} catch (error) {
|
|
477
|
+
const message = error instanceof Error ? error.message : "Verifizierung fehlgeschlagen";
|
|
478
|
+
if (message.includes("expired") || message.includes("abgelaufen")) {
|
|
479
|
+
log.error("2FA-Token abgelaufen. Bitte erneut anmelden.");
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
483
|
+
log.error(`${message}. Noch ${MAX_ATTEMPTS - attempt} Versuche.`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
log.error("2FA-Verifizierung fehlgeschlagen");
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
464
490
|
async function findAvailablePort() {
|
|
465
491
|
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
466
492
|
const isAvailable = await checkPort(port);
|
|
@@ -71,6 +71,16 @@ var ApiClient = class {
|
|
|
71
71
|
body: JSON.stringify({ token, otp })
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate 2FA code during login
|
|
76
|
+
* Used when initial login returns requires2FA: true
|
|
77
|
+
*/
|
|
78
|
+
async validate2FALogin(data) {
|
|
79
|
+
return this.request("/auth/2fa/login-validate", {
|
|
80
|
+
method: "POST",
|
|
81
|
+
body: JSON.stringify(data)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
74
84
|
async getCurrentUser() {
|
|
75
85
|
return this.request("/auth/me");
|
|
76
86
|
}
|