fchek 1.0.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/README.md +64 -0
- package/bin/fchek.js +107 -0
- package/lib/api.js +110 -0
- package/lib/audit.js +211 -0
- package/lib/bench.js +248 -0
- package/lib/config.js +191 -0
- package/lib/context.js +356 -0
- package/lib/convention.js +526 -0
- package/lib/coverage.js +604 -0
- package/lib/db.js +135 -0
- package/lib/deps-check.js +264 -0
- package/lib/deps.js +374 -0
- package/lib/docker.js +84 -0
- package/lib/doctor.js +149 -0
- package/lib/dom.js +226 -0
- package/lib/fuzz.js +470 -0
- package/lib/git.js +290 -0
- package/lib/goto.js +544 -0
- package/lib/launch.js +182 -0
- package/lib/lint.js +624 -0
- package/lib/new_features.test.js +181 -0
- package/lib/output.js +46 -0
- package/lib/port.js +173 -0
- package/lib/process.js +228 -0
- package/lib/profile.js +453 -0
- package/lib/python.js +41 -0
- package/lib/race.js +186 -0
- package/lib/registry.js +179 -0
- package/lib/repl.js +135 -0
- package/lib/run.js +403 -0
- package/lib/screenshot.js +152 -0
- package/lib/secrets.js +257 -0
- package/lib/state.js +219 -0
- package/lib/test.js +471 -0
- package/lib/vuln.js +253 -0
- package/lib/watch.js +240 -0
- package/lib/winlog.js +123 -0
- package/package.json +27 -0
- package/skills/ACTIVATE.md +274 -0
- package/skills/README.md +163 -0
- package/skills/agent.md +444 -0
- package/skills/api.md +47 -0
- package/skills/bench.md +117 -0
- package/skills/context.md +116 -0
- package/skills/convention.md +143 -0
- package/skills/coverage.md +99 -0
- package/skills/csharp.md +97 -0
- package/skills/db.md +66 -0
- package/skills/deps-check.md +135 -0
- package/skills/deps.md +143 -0
- package/skills/docker.md +61 -0
- package/skills/dom.md +56 -0
- package/skills/fuzz.md +167 -0
- package/skills/goto.md +111 -0
- package/skills/lint.md +123 -0
- package/skills/port.md +57 -0
- package/skills/profile.md +91 -0
- package/skills/race.md +117 -0
- package/skills/repl.md +81 -0
- package/skills/rules.md +318 -0
- package/skills/run.md +135 -0
- package/skills/secrets.md +170 -0
- package/skills/security.md +360 -0
- package/skills/state.md +261 -0
- package/skills/vuln.md +57 -0
- package/skills/windows.md +320 -0
package/lib/registry.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const { output, ok, fail } = require('./output');
|
|
5
|
+
|
|
6
|
+
const HELP = `
|
|
7
|
+
fchek registry <action> <key> [value_name] [data] [--type=REG_SZ|REG_DWORD]
|
|
8
|
+
|
|
9
|
+
Read and write Windows Registry.
|
|
10
|
+
|
|
11
|
+
Actions:
|
|
12
|
+
get <key> [value_name] Read key or specific value
|
|
13
|
+
set <key> <name> <data> Write a value
|
|
14
|
+
delete <key> [value_name] Delete key or value
|
|
15
|
+
exists <key> [value_name] Check if exists
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
fchek registry get HKCU\\Software\\Vertex
|
|
19
|
+
fchek registry get HKCU\\Software\\Vertex Theme
|
|
20
|
+
fchek registry set HKCU\\Software\\Vertex Theme dark
|
|
21
|
+
fchek registry exists HKCU\\Software\\Vertex
|
|
22
|
+
fchek registry delete HKCU\\Software\\Vertex
|
|
23
|
+
`.trim();
|
|
24
|
+
|
|
25
|
+
// Run a PowerShell script file (avoids all escaping issues)
|
|
26
|
+
function runScript(lines, timeoutMs) {
|
|
27
|
+
const os = require('os');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const fs = require('fs');
|
|
30
|
+
const script = lines.join('\r\n');
|
|
31
|
+
const tmp = path.join(os.tmpdir(), 'fchek_reg_' + process.pid + '.ps1');
|
|
32
|
+
fs.writeFileSync(tmp, script, 'utf8');
|
|
33
|
+
const res = spawnSync('powershell', [
|
|
34
|
+
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', tmp,
|
|
35
|
+
], { encoding: 'utf8', timeout: timeoutMs || 10000, windowsHide: true });
|
|
36
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
37
|
+
return res;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function toPs(key) {
|
|
41
|
+
return key
|
|
42
|
+
.replace(/^HKEY_CURRENT_USER\\/i, 'HKCU:\\')
|
|
43
|
+
.replace(/^HKEY_LOCAL_MACHINE\\/i, 'HKLM:\\')
|
|
44
|
+
.replace(/^HKEY_CLASSES_ROOT\\/i, 'HKCR:\\')
|
|
45
|
+
.replace(/^HKEY_USERS\\/i, 'HKU:\\')
|
|
46
|
+
.replace(/^HKCU\\/i, 'HKCU:\\')
|
|
47
|
+
.replace(/^HKLM\\/i, 'HKLM:\\');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseResult(res) {
|
|
51
|
+
const raw = (res.stdout || '').trim();
|
|
52
|
+
if (!raw) return null;
|
|
53
|
+
const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
|
|
54
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
55
|
+
if (lines[i].startsWith('{') || lines[i].startsWith('[')) {
|
|
56
|
+
try { return JSON.parse(lines[i]); } catch {}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function run(args) {
|
|
63
|
+
if (args.length === 0 || args[0] === '--help') { console.log(HELP); return; }
|
|
64
|
+
if (process.platform !== 'win32') return output(fail('fchek registry is Windows-only.'));
|
|
65
|
+
|
|
66
|
+
const action = args[0];
|
|
67
|
+
const key = args[1];
|
|
68
|
+
if (!key) return output(fail('Registry key required.'));
|
|
69
|
+
|
|
70
|
+
const psPath = toPs(key);
|
|
71
|
+
|
|
72
|
+
if (action === 'get') {
|
|
73
|
+
const vname = args[2] || null;
|
|
74
|
+
const lines = vname ? [
|
|
75
|
+
'$ErrorActionPreference = "SilentlyContinue"',
|
|
76
|
+
'$p = "' + psPath.replace(/"/g, '`"') + '"',
|
|
77
|
+
'$n = "' + (vname).replace(/"/g, '`"') + '"',
|
|
78
|
+
'$v = Get-ItemProperty -Path $p -Name $n -ErrorAction SilentlyContinue',
|
|
79
|
+
'if ($v) {',
|
|
80
|
+
' $d = $v.$n',
|
|
81
|
+
' Write-Output (@{exists=$true; key="' + key.replace(/"/g, '\\"') + '"; value=$n; data="$d"} | ConvertTo-Json -Compress)',
|
|
82
|
+
'} else {',
|
|
83
|
+
' Write-Output \'{"exists":false,"error":"Value not found"}\'',
|
|
84
|
+
'}',
|
|
85
|
+
] : [
|
|
86
|
+
'$ErrorActionPreference = "SilentlyContinue"',
|
|
87
|
+
'$p = "' + psPath.replace(/"/g, '`"') + '"',
|
|
88
|
+
'$item = Get-Item -Path $p -ErrorAction SilentlyContinue',
|
|
89
|
+
'if ($item) {',
|
|
90
|
+
' $vals = @{}',
|
|
91
|
+
' foreach ($n in $item.GetValueNames()) { $vals[$n] = "$($item.GetValue($n))" }',
|
|
92
|
+
' $subs = @($item.GetSubKeyNames())',
|
|
93
|
+
' @{exists=$true; key="' + key.replace(/"/g, '\\"') + '"; values=$vals; subkeys=$subs} | ConvertTo-Json -Depth 3 -Compress',
|
|
94
|
+
'} else {',
|
|
95
|
+
' Write-Output \'{"exists":false,"error":"Key not found"}\'',
|
|
96
|
+
'}',
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const res = runScript(lines);
|
|
100
|
+
const data = parseResult(res);
|
|
101
|
+
if (!data) return output(fail('Registry read failed: ' + (res.stderr || res.stdout || '').slice(0, 300)));
|
|
102
|
+
if (!data.exists) return output(fail(data.error || 'Not found'));
|
|
103
|
+
output(ok(data));
|
|
104
|
+
|
|
105
|
+
} else if (action === 'set') {
|
|
106
|
+
const vname = args[2];
|
|
107
|
+
const vdata = args[3];
|
|
108
|
+
const type = (args.find(a => a.startsWith('--type=')) || '--type=REG_SZ').replace('--type=', '');
|
|
109
|
+
if (!vname || vdata === undefined) return output(fail('Usage: fchek registry set <key> <name> <data>'));
|
|
110
|
+
|
|
111
|
+
// Map REG_* names to PowerShell RegistryValueKind names
|
|
112
|
+
const typeMap = {
|
|
113
|
+
'REG_SZ': 'String',
|
|
114
|
+
'REG_DWORD': 'DWord',
|
|
115
|
+
'REG_QWORD': 'QWord',
|
|
116
|
+
'REG_BINARY': 'Binary',
|
|
117
|
+
'REG_EXPAND_SZ': 'ExpandString',
|
|
118
|
+
'REG_MULTI_SZ': 'MultiString',
|
|
119
|
+
};
|
|
120
|
+
const psType = typeMap[type.toUpperCase()] || type;
|
|
121
|
+
|
|
122
|
+
const lines = [
|
|
123
|
+
'$ErrorActionPreference = "Stop"',
|
|
124
|
+
'try {',
|
|
125
|
+
' $p = "' + psPath.replace(/"/g, '`"') + '"',
|
|
126
|
+
' if (!(Test-Path $p)) { New-Item -Path $p -Force | Out-Null }',
|
|
127
|
+
' Set-ItemProperty -Path $p -Name "' + vname.replace(/"/g, '`"') + '" -Value "' + vdata.replace(/"/g, '`"') + '" -Type "' + psType + '" -Force',
|
|
128
|
+
' Write-Output \'{"success":true}\'',
|
|
129
|
+
'} catch {',
|
|
130
|
+
' Write-Output (@{success=$false; error=$_.Exception.Message} | ConvertTo-Json -Compress)',
|
|
131
|
+
'}',
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
const res = runScript(lines);
|
|
135
|
+
const data = parseResult(res);
|
|
136
|
+
if (!data) return output(fail('Set failed: ' + (res.stderr || res.stdout || '').slice(0, 300)));
|
|
137
|
+
if (!data.success) return output(fail(data.error || 'Set failed'));
|
|
138
|
+
output(ok({ success: true, key, value: vname, data: vdata, type }));
|
|
139
|
+
|
|
140
|
+
} else if (action === 'delete') {
|
|
141
|
+
const vname = args[2] || null;
|
|
142
|
+
const lines = vname ? [
|
|
143
|
+
'try {',
|
|
144
|
+
' Remove-ItemProperty -Path "' + psPath.replace(/"/g, '`"') + '" -Name "' + vname.replace(/"/g, '`"') + '" -Force -ErrorAction Stop',
|
|
145
|
+
' Write-Output \'{"deleted":true}\'',
|
|
146
|
+
'} catch { Write-Output \'{"deleted":false}\' }',
|
|
147
|
+
] : [
|
|
148
|
+
'try {',
|
|
149
|
+
' Remove-Item -Path "' + psPath.replace(/"/g, '`"') + '" -Recurse -Force -ErrorAction Stop',
|
|
150
|
+
' Write-Output \'{"deleted":true}\'',
|
|
151
|
+
'} catch { Write-Output \'{"deleted":false}\' }',
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const res = runScript(lines);
|
|
155
|
+
const data = parseResult(res);
|
|
156
|
+
if (!data || !data.deleted) return output(fail('Delete failed: ' + (res.stderr || res.stdout || '').slice(0, 200)));
|
|
157
|
+
output(ok({ deleted: true, key, value: vname }));
|
|
158
|
+
|
|
159
|
+
} else if (action === 'exists') {
|
|
160
|
+
const vname = args[2] || null;
|
|
161
|
+
const lines = vname ? [
|
|
162
|
+
'$e = (Get-ItemProperty -Path "' + psPath.replace(/"/g, '`"') + '" -Name "' + vname.replace(/"/g, '`"') + '" -ErrorAction SilentlyContinue) -ne $null',
|
|
163
|
+
'Write-Output (@{exists=$e} | ConvertTo-Json -Compress)',
|
|
164
|
+
] : [
|
|
165
|
+
'$e = Test-Path "' + psPath.replace(/"/g, '`"') + '"',
|
|
166
|
+
'Write-Output (@{exists=$e} | ConvertTo-Json -Compress)',
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const res = runScript(lines);
|
|
170
|
+
const data = parseResult(res);
|
|
171
|
+
if (!data) return output(fail('Registry check failed: ' + (res.stderr || res.stdout || '').slice(0, 200)));
|
|
172
|
+
output(ok({ exists: data.exists, key, value: vname }));
|
|
173
|
+
|
|
174
|
+
} else {
|
|
175
|
+
output(fail('Unknown action: "' + action + '". Valid: get, set, delete, exists'));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { run };
|
package/lib/repl.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* repl.js — interactive REPL with live in-memory state.
|
|
5
|
+
*
|
|
6
|
+
* Difference from state.js:
|
|
7
|
+
* repl = in-process memory (lives only while this process runs)
|
|
8
|
+
* state = disk-based memory (survives restarts)
|
|
9
|
+
*
|
|
10
|
+
* Use repl when you need fast iterative work in one session.
|
|
11
|
+
* Use state when you need context to persist across sessions.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const readline = require('readline');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { ok, fail, output } = require('./output');
|
|
17
|
+
|
|
18
|
+
const BANNER = `
|
|
19
|
+
╔══════════════════════════════════════════╗
|
|
20
|
+
║ fchek repl — interactive mode ║
|
|
21
|
+
║ type "help" for commands, "exit" to quit ║
|
|
22
|
+
╚══════════════════════════════════════════╝
|
|
23
|
+
`.trim();
|
|
24
|
+
|
|
25
|
+
const REPL_HELP = `
|
|
26
|
+
Available commands (same as fchek CLI, but run interactively):
|
|
27
|
+
|
|
28
|
+
profile <file> [app|web] Profile code
|
|
29
|
+
race <file.cpp> [--asan] Check sanitizers
|
|
30
|
+
goto <file:line> LSP definition + references
|
|
31
|
+
state <action> [key] [val] Persistent disk state
|
|
32
|
+
set <key> <value> Set in-session variable (memory only)
|
|
33
|
+
get <key> Get in-session variable
|
|
34
|
+
vars List all in-session variables
|
|
35
|
+
clear Clear screen
|
|
36
|
+
help This message
|
|
37
|
+
exit / quit Exit REPL
|
|
38
|
+
`.trim();
|
|
39
|
+
|
|
40
|
+
async function run(args) {
|
|
41
|
+
// In-memory session variables (lost when REPL exits)
|
|
42
|
+
const sessionVars = {};
|
|
43
|
+
|
|
44
|
+
const rl = readline.createInterface({
|
|
45
|
+
input: process.stdin,
|
|
46
|
+
output: process.stdout,
|
|
47
|
+
prompt: 'fchek> ',
|
|
48
|
+
historySize: 100,
|
|
49
|
+
completer: (line) => {
|
|
50
|
+
const completions = ['profile', 'race', 'goto', 'state', 'set', 'get', 'vars', 'help', 'exit', 'clear'];
|
|
51
|
+
const hits = completions.filter(c => c.startsWith(line));
|
|
52
|
+
return [hits.length ? hits : completions, line];
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log(BANNER);
|
|
57
|
+
rl.prompt();
|
|
58
|
+
|
|
59
|
+
rl.on('line', async (rawLine) => {
|
|
60
|
+
const line = rawLine.trim();
|
|
61
|
+
if (!line) { rl.prompt(); return; }
|
|
62
|
+
|
|
63
|
+
const parts = line.split(/\s+/);
|
|
64
|
+
const cmd = parts[0].toLowerCase();
|
|
65
|
+
const rest = parts.slice(1);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
switch (cmd) {
|
|
69
|
+
case 'exit':
|
|
70
|
+
case 'quit':
|
|
71
|
+
console.log('Bye.');
|
|
72
|
+
rl.close();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'help':
|
|
77
|
+
console.log('\n' + REPL_HELP + '\n');
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 'clear':
|
|
81
|
+
process.stdout.write('\x1Bc');
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case 'set': {
|
|
85
|
+
const key = rest[0];
|
|
86
|
+
const value = rest.slice(1).join(' ');
|
|
87
|
+
if (!key) { console.log('Usage: set <key> <value>'); break; }
|
|
88
|
+
sessionVars[key] = value;
|
|
89
|
+
console.log(JSON.stringify(ok({ set: key, value }, 'repl')));
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
case 'get': {
|
|
94
|
+
const key = rest[0];
|
|
95
|
+
if (!key) { console.log('Usage: get <key>'); break; }
|
|
96
|
+
if (!(key in sessionVars)) {
|
|
97
|
+
console.log(JSON.stringify(fail(`No session var: "${key}"`, 'repl')));
|
|
98
|
+
} else {
|
|
99
|
+
console.log(JSON.stringify(ok({ key, value: sessionVars[key] }, 'repl')));
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'vars':
|
|
105
|
+
console.log(JSON.stringify(ok({ session_vars: sessionVars }, 'repl')));
|
|
106
|
+
break;
|
|
107
|
+
|
|
108
|
+
// Delegate to lib/ commands — same as CLI
|
|
109
|
+
case 'profile':
|
|
110
|
+
case 'race':
|
|
111
|
+
case 'goto':
|
|
112
|
+
case 'state': {
|
|
113
|
+
const handler = require(`./${cmd}`);
|
|
114
|
+
// Redirect console.log output captured inline
|
|
115
|
+
await handler.run(rest);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
default:
|
|
120
|
+
console.log(JSON.stringify(fail(`Unknown command: "${cmd}". Type "help".`, 'repl')));
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.log(JSON.stringify(fail(`Error: ${err.message}`, 'repl')));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
rl.prompt();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
rl.on('close', () => {
|
|
130
|
+
console.log('\nSession ended.');
|
|
131
|
+
process.exit(0);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { run };
|