micro-models-agent 2.11.0 → 2.12.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/CHANGELOG.md +10 -0
- package/dist/assets/js/tabs.js +22 -3
- package/dist/cli/cache-line.js +1 -0
- package/dist/cli/command-suggest.js +5 -4
- package/dist/cli/commands.js +231 -122
- package/dist/cli/completer.js +132 -31
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-payload.js +4 -4
- package/dist/cli/main.js +166 -61
- package/dist/cli/onboarding-commands.js +126 -0
- package/dist/cli/plugin-commands.js +2 -1
- package/dist/cli/relaunch.js +16 -13
- package/dist/cli/repl-commands-lsp.js +75 -0
- package/dist/cli/repl-commands-skills.js +105 -0
- package/dist/cli/repl-commands.js +589 -266
- package/dist/cli/repl.js +436 -177
- package/dist/cli/run-result.js +5 -6
- package/dist/cli/security-commands.js +37 -28
- package/dist/cli/setup-order.js +4 -55
- package/dist/cli/setup-prompt.js +3 -2
- package/dist/cli/setup.js +27 -107
- package/dist/cli/web-command.js +98 -0
- package/dist/config/budget.js +5 -5
- package/dist/config/config.js +103 -96
- package/dist/config/defaults.js +38 -8
- package/dist/config/domains.js +24 -27
- package/dist/config/env.js +46 -0
- package/dist/config/experts.js +2 -0
- package/dist/config/index.js +1 -0
- package/dist/config/paths.js +56 -0
- package/dist/config/redact.js +44 -0
- package/dist/config/registry-extra.js +334 -0
- package/dist/config/registry.js +584 -0
- package/dist/config/security.js +35 -36
- package/dist/config/timeouts.js +74 -0
- package/dist/config/utils.js +16 -15
- package/dist/core/agent/artifact-progress.js +59 -0
- package/dist/core/agent/artifact-resolve.js +79 -0
- package/dist/core/agent/audit-gate.js +7 -3
- package/dist/core/agent/compaction.js +2 -2
- package/dist/core/agent/constants.js +33 -4
- package/dist/core/agent/context-log.js +454 -0
- package/dist/core/agent/context-renderer.js +5 -0
- package/dist/core/agent/critic-gate.js +132 -0
- package/dist/core/agent/hallucination-gate.js +148 -38
- package/dist/core/agent/loop-state.js +15 -0
- package/dist/core/agent/prefix-monitor.js +1 -1
- package/dist/core/agent/reasoning-resolver.js +10 -1
- package/dist/core/agent/token-tracker.js +7 -4
- package/dist/core/agent/tool-batch.js +93 -18
- package/dist/core/agent/tool-images.js +18 -0
- package/dist/core/agent/tool-output.js +65 -11
- package/dist/core/agent/tool-pairing.js +60 -0
- package/dist/core/agent-moe.js +25 -24
- package/dist/core/agent.js +429 -108
- package/dist/core/bootstrap/agent-phase.js +66 -0
- package/dist/core/bootstrap/config-phase.js +86 -0
- package/dist/core/bootstrap/modules-phase.js +269 -0
- package/dist/core/bootstrap/provider-phase.js +183 -0
- package/dist/core/bootstrap/session-phase.js +204 -0
- package/dist/core/bootstrap/state.js +94 -0
- package/dist/core/bootstrap/workspace-phase.js +115 -0
- package/dist/core/bootstrap.js +92 -660
- package/dist/core/crash-handler.js +17 -7
- package/dist/core/environment.js +37 -12
- package/dist/core/host-plan.js +43 -0
- package/dist/core/host.js +23 -0
- package/dist/core/index.js +1 -0
- package/dist/core/prompt-builder.js +17 -1
- package/dist/core/prompt-overflow.js +42 -43
- package/dist/core/session-logger.js +122 -30
- package/dist/core/system-prompt.js +117 -0
- package/dist/core/version.js +6 -4
- package/dist/core/web-host.js +34 -0
- package/dist/core/workspace.js +19 -15
- package/dist/i18n/index.js +37 -3
- package/dist/index.js +7 -3
- package/dist/llm/image-utils.js +55 -64
- package/dist/llm/llm-errors.js +9 -0
- package/dist/llm/model-loader.js +10 -32
- package/dist/llm/models-dev.js +121 -0
- package/dist/llm/openai-compat.js +146 -73
- package/dist/llm/orchestrator.js +33 -11
- package/dist/llm/provider-budget.js +2 -2
- package/dist/llm/provider-context.js +233 -0
- package/dist/llm/provider.js +1 -1
- package/dist/llm/response.js +5 -0
- package/dist/llm/token-counter.js +27 -11
- package/dist/logger/app-logger.js +48 -22
- package/dist/logger/file-log.js +28 -19
- package/dist/logger/index.js +1 -0
- package/dist/main.js +273 -47
- package/dist/migration/backup.js +14 -3
- package/dist/migration/detect.js +12 -3
- package/dist/migration/index.js +1 -0
- package/dist/modules/artifacts/store.js +9 -8
- package/dist/modules/browser/actions.js +6 -0
- package/dist/modules/browser/bridge-client.js +14 -12
- package/dist/modules/browser/bridge-path.js +4 -4
- package/dist/modules/browser/console-buffer.js +37 -0
- package/dist/modules/browser/cookie-store.js +5 -0
- package/dist/modules/browser/driver-types.js +1 -0
- package/dist/modules/browser/driver.js +19 -11
- package/dist/modules/browser/index.js +3 -1
- package/dist/modules/browser/module.js +2 -1
- package/dist/modules/browser/session.js +10 -30
- package/dist/modules/browser/snapshot.js +19 -13
- package/dist/modules/browser/types.js +1 -0
- package/dist/modules/certification/cli.js +28 -20
- package/dist/modules/certification/fact-checker.js +9 -1
- package/dist/modules/certification/loader.js +9 -2
- package/dist/modules/certification/manifest.js +83 -21
- package/dist/modules/certification/runner.js +23 -13
- package/dist/modules/certification/scenarios.js +20 -20
- package/dist/modules/certification/syntax-scenarios.js +1 -2
- package/dist/modules/context/chunk-query.js +11 -9
- package/dist/modules/context/fact-extractor.js +37 -39
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +106 -80
- package/dist/modules/critic/client.js +176 -0
- package/dist/modules/critic/evidence.js +81 -0
- package/dist/modules/critic/provider.js +45 -0
- package/dist/modules/critic/tools.js +78 -0
- package/dist/modules/critic/types.js +1 -0
- package/dist/modules/execution/audit-runners.js +28 -34
- package/dist/modules/execution/auditor.js +53 -49
- package/dist/modules/execution/execution-plugin.js +142 -102
- package/dist/modules/execution/index.js +1 -1
- package/dist/modules/execution/input-from.js +37 -13
- package/dist/modules/execution/module.js +368 -191
- package/dist/modules/execution/moe-executor.js +53 -26
- package/dist/modules/execution/plan-actions.js +320 -202
- package/dist/modules/execution/plan-checks.js +35 -0
- package/dist/modules/execution/plan-coverage.js +36 -33
- package/dist/modules/execution/plan-steps.js +11 -3
- package/dist/modules/execution/plan-store.js +83 -17
- package/dist/modules/execution/plan-tool.js +89 -35
- package/dist/modules/execution/plan-validator.js +30 -10
- package/dist/modules/execution/planner.js +22 -13
- package/dist/modules/execution/stuck-detector.js +196 -93
- package/dist/modules/execution/tracker.js +22 -8
- package/dist/modules/execution/verifier.js +69 -48
- package/dist/modules/execution/windows-commands.js +12 -12
- package/dist/modules/hallucination/artifact-claim.js +93 -0
- package/dist/modules/hallucination/confidence.js +13 -11
- package/dist/modules/hallucination/consistency.js +10 -2
- package/dist/modules/hallucination/detector.js +10 -5
- package/dist/modules/hallucination/factual.js +43 -26
- package/dist/modules/hallucination/index.js +1 -0
- package/dist/modules/hallucination/js-identifiers.js +44 -45
- package/dist/modules/hallucination/llm-judge.js +17 -12
- package/dist/modules/hallucination/success-claim.js +59 -0
- package/dist/modules/indexer/cache.js +52 -4
- package/dist/modules/indexer/index.js +1 -0
- package/dist/modules/indexer/map-command.js +2 -4
- package/dist/modules/indexer/map-select.js +16 -16
- package/dist/modules/indexer/module.js +47 -12
- package/dist/modules/indexer/project-profile.js +8 -7
- package/dist/modules/indexer/symbols.js +15 -15
- package/dist/modules/indexer/walker.js +159 -29
- package/dist/modules/lsp/check-tool.js +7 -5
- package/dist/modules/lsp/client.js +39 -39
- package/dist/modules/lsp/command.js +17 -24
- package/dist/modules/lsp/config.js +12 -11
- package/dist/modules/lsp/index.js +1 -0
- package/dist/modules/lsp/module.js +48 -46
- package/dist/modules/lsp/probe.js +13 -14
- package/dist/modules/lsp/project-root.js +14 -17
- package/dist/modules/lsp/startup-check.js +18 -16
- package/dist/modules/mcp/client.js +59 -62
- package/dist/modules/mcp/index.js +1 -0
- package/dist/modules/mcp/module.js +66 -37
- package/dist/modules/mcp/registry.js +5 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +12 -8
- package/dist/modules/memory/search.js +4 -1
- package/dist/modules/memory/store.js +40 -2
- package/dist/modules/onboarding/init.js +47 -0
- package/dist/modules/onboarding/meet.js +30 -0
- package/dist/modules/onboarding/mode.js +30 -0
- package/dist/modules/onboarding/prompts.js +43 -0
- package/dist/modules/pipelines/engine.js +10 -0
- package/dist/modules/pipelines/index.js +1 -0
- package/dist/modules/pipelines/parser.js +4 -1
- package/dist/modules/pipelines/template.js +4 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
- package/dist/modules/plugins/builtin/notify.js +2 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +18 -3
- package/dist/modules/plugins/manager.js +37 -17
- package/dist/modules/pricing/index.js +10 -11
- package/dist/modules/pricing/prices.js +12 -12
- package/dist/modules/processes/index.js +1 -0
- package/dist/modules/processes/kill-tree.js +27 -31
- package/dist/modules/processes/registry.js +44 -25
- package/dist/modules/processes/runner.js +4 -4
- package/dist/modules/providers/capabilities.js +28 -0
- package/dist/modules/providers/create.js +3 -4
- package/dist/modules/providers/factory.js +7 -7
- package/dist/modules/providers/fallback.js +12 -12
- package/dist/modules/providers/health.js +4 -4
- package/dist/modules/providers/manager.js +50 -19
- package/dist/modules/providers/naming.js +15 -0
- package/dist/modules/providers/preflight.js +71 -0
- package/dist/modules/providers/presets.js +39 -8
- package/dist/modules/providers/registry.js +7 -2
- package/dist/modules/reasoning/policy.js +8 -6
- package/dist/modules/reasoning/probe.js +30 -25
- package/dist/modules/security/audit-log.js +19 -36
- package/dist/modules/security/audit-notifier.js +35 -75
- package/dist/modules/security/command-validator.js +99 -51
- package/dist/modules/security/content-scanner.js +8 -8
- package/dist/modules/security/data-sanitizer.js +9 -16
- package/dist/modules/security/encryption.js +32 -52
- package/dist/modules/security/index.js +2 -1
- package/dist/modules/security/network-validator.js +120 -43
- package/dist/modules/security/path-validator.js +61 -46
- package/dist/modules/security/protected-paths.js +73 -0
- package/dist/modules/security/rate-limiter.js +17 -49
- package/dist/modules/security/security-policies.js +25 -57
- package/dist/modules/security/session-encryption.js +32 -71
- package/dist/modules/security/session-isolation.js +15 -28
- package/dist/modules/session/auto-name.js +63 -0
- package/dist/modules/session/export.js +98 -0
- package/dist/modules/session/index.js +1 -0
- package/dist/modules/session/manager.js +79 -28
- package/dist/modules/session/module.js +4 -3
- package/dist/modules/session/store.js +57 -20
- package/dist/modules/setup/answers.js +35 -0
- package/dist/modules/setup/apply.js +79 -0
- package/dist/modules/setup/discovery.js +72 -0
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/loader.js +9 -1
- package/dist/modules/skills/module.js +11 -0
- package/dist/modules/updater/changelog-reader.js +15 -16
- package/dist/modules/updater/checker.js +9 -4
- package/dist/modules/updater/dev-detect.js +7 -6
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/updater/module.js +17 -15
- package/dist/modules/user-profile/compressor.js +2 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +10 -1
- package/dist/modules/webui/answer-api.js +29 -0
- package/dist/modules/webui/assets.js +50 -0
- package/dist/modules/webui/body.js +21 -0
- package/dist/modules/webui/commands-settings.js +209 -0
- package/dist/modules/webui/commands-shared.js +60 -0
- package/dist/modules/webui/commands.js +174 -0
- package/dist/modules/webui/config-api.js +134 -0
- package/dist/modules/webui/file-api.js +202 -0
- package/dist/modules/webui/forward.js +159 -0
- package/dist/modules/webui/history-api.js +112 -0
- package/dist/modules/webui/host-state.js +60 -0
- package/dist/modules/webui/i18n-api.js +9 -0
- package/dist/modules/webui/limits.js +6 -0
- package/dist/modules/webui/log-forward.js +20 -0
- package/dist/modules/webui/memory-commands.js +64 -0
- package/dist/modules/webui/model-cache.js +18 -0
- package/dist/modules/webui/module.js +148 -0
- package/dist/modules/webui/persist.js +24 -0
- package/dist/modules/webui/prompt-io.js +97 -0
- package/dist/modules/webui/providers-api.js +337 -0
- package/dist/modules/webui/providers-view.js +56 -0
- package/dist/modules/webui/reasoning-view.js +15 -0
- package/dist/modules/webui/run-agent.js +81 -0
- package/dist/modules/webui/server.js +284 -0
- package/dist/modules/webui/sessions-api.js +178 -0
- package/dist/modules/webui/settings-api.js +561 -0
- package/dist/modules/webui/setup-api.js +201 -0
- package/dist/modules/webui/state.js +74 -0
- package/dist/modules/webui/util.js +261 -0
- package/dist/output/bus.js +7 -2
- package/dist/output/channel.js +80 -28
- package/dist/output/format.js +2 -1
- package/dist/output/index.js +1 -0
- package/dist/output/json-sink.js +5 -3
- package/dist/output/machine.js +4 -3
- package/dist/output/session-sink.js +4 -4
- package/dist/tools/approve.js +4 -0
- package/dist/tools/attach-image.js +5 -4
- package/dist/tools/auto-fixer.js +26 -44
- package/dist/tools/auto-tags.js +83 -0
- package/dist/tools/bash.js +193 -116
- package/dist/tools/browser.js +76 -31
- package/dist/tools/chunk-query.js +14 -6
- package/dist/tools/create-dir.js +9 -5
- package/dist/tools/delete-file.js +9 -5
- package/dist/tools/download-file.js +32 -7
- package/dist/tools/edit-file.js +52 -14
- package/dist/tools/enable-tools.js +6 -6
- package/dist/tools/executor.js +132 -27
- package/dist/tools/file-info.js +3 -1
- package/dist/tools/filter-tools.js +3 -3
- package/dist/tools/glob-tool.js +4 -1
- package/dist/tools/grep-tool.js +13 -10
- package/dist/tools/hidden-tools-block.js +6 -6
- package/dist/tools/index.js +16 -8
- package/dist/tools/list-dir.js +4 -2
- package/dist/tools/load-skill.js +7 -0
- package/dist/tools/mcp-call.js +15 -4
- package/dist/tools/meet-done.js +14 -0
- package/dist/tools/move-file.js +14 -7
- package/dist/tools/path-utils.js +29 -29
- package/dist/tools/pipeline-run.js +29 -7
- package/dist/tools/preview.js +1 -1
- package/dist/tools/process-ids.js +31 -0
- package/dist/tools/process-kill.js +9 -16
- package/dist/tools/process-list.js +1 -0
- package/dist/tools/process-log.js +11 -16
- package/dist/tools/process-wait.js +63 -0
- package/dist/tools/prompt-io.js +4 -3
- package/dist/tools/question.js +172 -25
- package/dist/tools/read-file.js +55 -34
- package/dist/tools/recall.js +14 -8
- package/dist/tools/registry.js +6 -5
- package/dist/tools/remember.js +8 -4
- package/dist/tools/scope-request.js +11 -6
- package/dist/tools/search-history.js +5 -2
- package/dist/tools/session-info.js +2 -0
- package/dist/tools/set-thinking.js +8 -4
- package/dist/tools/subagent.js +59 -39
- package/dist/tools/syntax-validator.js +292 -90
- package/dist/tools/user-input.js +22 -18
- package/dist/tools/web-browse.js +9 -3
- package/dist/tools/web-fetch.js +12 -23
- package/dist/tools/web-search.js +7 -5
- package/dist/tools/write-file.js +45 -15
- package/dist/ui/action-log.js +13 -0
- package/dist/ui/box.js +4 -3
- package/dist/ui/colors.js +2 -0
- package/dist/ui/diff.js +6 -5
- package/dist/ui/index.js +1 -0
- package/dist/ui/line-editor.js +230 -122
- package/dist/ui/line-math.js +10 -2
- package/dist/ui/md-formatter.js +57 -37
- package/dist/ui/output.js +8 -3
- package/dist/ui/plan-view.js +21 -10
- package/dist/ui/renderer.js +393 -67
- package/dist/ui/spinner.js +16 -5
- package/dist/ui/table.js +15 -17
- package/dist/ui/theme.js +143 -0
- package/dist/utils/child-run.js +20 -0
- package/dist/utils/error.js +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/retry.js +2 -2
- package/dist/utils/truncate.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_SECURITY_CONFIG } from "../../config/security";
|
|
2
|
-
//
|
|
2
|
+
// Резерв, если DEFAULT_SECURITY_CONFIG недоступен
|
|
3
3
|
const FALLBACK_BASH_CONFIG = {
|
|
4
4
|
enabled: false,
|
|
5
5
|
blacklist: [
|
|
@@ -24,77 +24,125 @@ const FALLBACK_BASH_CONFIG = {
|
|
|
24
24
|
};
|
|
25
25
|
export const DEFAULT_BASH_CONFIG = DEFAULT_SECURITY_CONFIG?.bash || FALLBACK_BASH_CONFIG;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* Выделить базовую команду из shell-строки.
|
|
28
|
+
* Учитывает пути, env-присваивания и sudo.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
30
|
+
* Примеры:
|
|
31
31
|
* "/usr/bin/rm -rf /" → "rm"
|
|
32
32
|
* "sudo /usr/bin/rm file" → "rm"
|
|
33
33
|
* "NODE_ENV=prod node app.js" → "node"
|
|
34
34
|
* "env PATH=/x rm -rf /" → "rm"
|
|
35
|
-
* "cmd=rm; $cmd -rf /" → "cmd" (
|
|
35
|
+
* "cmd=rm; $cmd -rf /" → "cmd" (переменная не раскрывается)
|
|
36
36
|
* "powershell Remove-Item x" → "Remove-Item"
|
|
37
37
|
* "cmd /c del /s /q" → "del"
|
|
38
38
|
*/
|
|
39
|
+
/** Обёртки, запускающие другую команду; их опции пропускаются. */
|
|
40
|
+
const WRAPPERS = new Set([
|
|
41
|
+
"sudo",
|
|
42
|
+
"doas",
|
|
43
|
+
"env",
|
|
44
|
+
"command",
|
|
45
|
+
"exec",
|
|
46
|
+
"nohup",
|
|
47
|
+
"nice",
|
|
48
|
+
"timeout",
|
|
49
|
+
"stdbuf",
|
|
50
|
+
"ionice",
|
|
51
|
+
"setsid",
|
|
52
|
+
"time",
|
|
53
|
+
]);
|
|
54
|
+
/** Флаги обёрток, съедающие следующий токен как значение. */
|
|
55
|
+
const WRAPPER_VALUE_FLAGS = {
|
|
56
|
+
sudo: new Set(["-u", "-g", "-p", "-C", "-U", "-h", "-r", "-t", "-c"]),
|
|
57
|
+
doas: new Set(["-u"]),
|
|
58
|
+
nice: new Set(["-n", "--adjustment"]),
|
|
59
|
+
timeout: new Set(["-k", "-s", "--signal", "--kill-after"]),
|
|
60
|
+
stdbuf: new Set(["-i", "-o", "-e"]),
|
|
61
|
+
ionice: new Set(["-c", "-n"]),
|
|
62
|
+
};
|
|
63
|
+
/** Пропустить собственные опции/значения обёртки; вернуть следующий индекс. */
|
|
64
|
+
function skipWrapperArgs(tokens, i, wrapper) {
|
|
65
|
+
const valueFlags = WRAPPER_VALUE_FLAGS[wrapper] ?? new Set();
|
|
66
|
+
while (i < tokens.length) {
|
|
67
|
+
const t = tokens[i];
|
|
68
|
+
if (t.startsWith("-")) {
|
|
69
|
+
i++;
|
|
70
|
+
if (t.includes("="))
|
|
71
|
+
continue; // --flag=value самодостаточен
|
|
72
|
+
if (valueFlags.has(t))
|
|
73
|
+
i++; // съесть значение флага
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Голое число (timeout 5, nice 10) — никогда не команда.
|
|
77
|
+
if (/^\d+(?:\.\d+)?[smhd]?$/.test(t)) {
|
|
78
|
+
i++;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return i;
|
|
84
|
+
}
|
|
39
85
|
function extractBaseCommand(trimmed) {
|
|
40
86
|
const tokens = trimmed.split(/\s+/);
|
|
41
87
|
let i = 0;
|
|
42
|
-
//
|
|
88
|
+
// Пропуск env-присваиваний (KEY=val, KEY="val" и т.п.) и обёрток
|
|
89
|
+
// (sudo/nice/timeout/stdbuf/…) ВМЕСТЕ с их опциями — иначе
|
|
90
|
+
// `nice rm file` извлёк бы "nice", и blacklist не увидел бы `rm`.
|
|
43
91
|
while (i < tokens.length) {
|
|
44
92
|
const tok = tokens[i];
|
|
45
93
|
if (/^[a-zA-Z_][a-zA-Z0-9_]*=/.test(tok)) {
|
|
46
94
|
i++;
|
|
47
95
|
continue;
|
|
48
96
|
}
|
|
49
|
-
if (tok
|
|
50
|
-
i
|
|
97
|
+
if (WRAPPERS.has(tok)) {
|
|
98
|
+
i = skipWrapperArgs(tokens, i + 1, tok);
|
|
51
99
|
continue;
|
|
52
100
|
}
|
|
53
101
|
break;
|
|
54
102
|
}
|
|
55
103
|
if (i >= tokens.length)
|
|
56
104
|
return tokens[0] ?? "";
|
|
57
|
-
//
|
|
58
|
-
//
|
|
105
|
+
// basename из пути (/usr/bin/rm → rm). Сначала снять кавычки —
|
|
106
|
+
// иначе `'rm'` / `"rm"` полностью обойдут blacklist.
|
|
59
107
|
const raw = tokens[i].replace(/^["']+|["']+$/g, "");
|
|
60
108
|
const parts = raw.split(/[\\/]/);
|
|
61
109
|
const basename = parts[parts.length - 1] || raw;
|
|
62
|
-
//
|
|
110
|
+
// Срезать .exe/.cmd/.bat для сверки (powershell.exe → powershell)
|
|
63
111
|
return basename.replace(/\.(exe|cmd|bat)$/i, "") || basename;
|
|
64
112
|
}
|
|
65
113
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* "&"
|
|
69
|
-
* "|"
|
|
70
|
-
* ">"
|
|
71
|
-
* "2>"
|
|
114
|
+
* Есть ли shell-оператор отдельным токеном в команде.
|
|
115
|
+
* Regex с lookbehind/lookahead, чтобы:
|
|
116
|
+
* "&" НЕ матчил "&&" (AND)
|
|
117
|
+
* "|" НЕ матчил "||" (OR)
|
|
118
|
+
* ">" НЕ матчил ">>" (append)
|
|
119
|
+
* "2>" НЕ матчил "2>>" (append stderr)
|
|
72
120
|
*/
|
|
73
121
|
function containsOperator(command, op) {
|
|
74
122
|
const escaped = op.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
123
|
+
// Односимвольные операторы — префиксы многосимвольных:
|
|
124
|
+
// не матчить внутри длинной цепочки.
|
|
125
|
+
// "&" уже не в dangerousOperators (&& легитимен),
|
|
126
|
+
// спецкейс оставлен на случай ручного добавления.
|
|
79
127
|
if (op === "&")
|
|
80
128
|
return /(?<!&)&(?!&)/.test(command);
|
|
81
129
|
if (op === "|")
|
|
82
130
|
return /(?<!\|)\|(?!\|)/.test(command);
|
|
83
131
|
if (op === ">")
|
|
84
132
|
return /(?<!>)>(?!>)/.test(command);
|
|
85
|
-
//
|
|
86
|
-
// match literally.
|
|
133
|
+
// Остальное (">>", "2>", "2>>", ";", "`") — буквальное совпадение.
|
|
87
134
|
return new RegExp(escaped).test(command);
|
|
88
135
|
}
|
|
89
136
|
/**
|
|
90
|
-
*
|
|
137
|
+
* Разрешена ли команда по конфигу безопасности.
|
|
138
|
+
* Выключенная защита разрешает всё; пустая команда запрещена.
|
|
91
139
|
*/
|
|
92
140
|
export function isCommandAllowed(command, securityConfig) {
|
|
93
|
-
//
|
|
141
|
+
// Выключенная защита разрешает всё
|
|
94
142
|
if (!securityConfig?.enabled) {
|
|
95
143
|
return { allowed: true, sanitizedCommand: command };
|
|
96
144
|
}
|
|
97
|
-
//
|
|
145
|
+
// Конфиг должен иметь все поля
|
|
98
146
|
let config = securityConfig;
|
|
99
147
|
if (!config.blacklist || !Array.isArray(config.blacklist)) {
|
|
100
148
|
config = {
|
|
@@ -106,8 +154,8 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
106
154
|
if (!trimmedCommand) {
|
|
107
155
|
return { allowed: false, reason: "Empty command" };
|
|
108
156
|
}
|
|
109
|
-
// Shell
|
|
110
|
-
//
|
|
157
|
+
// Shell-обёртки запрещены всегда — они обходят всю валидацию.
|
|
158
|
+
// Агент использует тул bash напрямую, а не интерпретаторы.
|
|
111
159
|
const ALWAYS_BLOCKED = ["powershell", "pwsh", "cmd"];
|
|
112
160
|
const baseForShellCheck = extractBaseCommand(trimmedCommand);
|
|
113
161
|
if (ALWAYS_BLOCKED.includes(baseForShellCheck)) {
|
|
@@ -116,25 +164,25 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
116
164
|
reason: `Shell wrapper "${baseForShellCheck}" is blocked — use the bash tool directly`,
|
|
117
165
|
};
|
|
118
166
|
}
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
167
|
+
// Интерпретаторы с -c/-e исполняют произвольный код в обход blacklist.
|
|
168
|
+
// Кавычки снимаются заранее (`'bash' -c`), имя может идти после пути
|
|
169
|
+
// (`/bin/bash -c`) — матчить и их.
|
|
122
170
|
const unquotedCommand = trimmedCommand.replace(/["']/g, "");
|
|
123
|
-
const INTERPRETER_FLAGS = /(?:^|[\\/\s])(?:bash|sh|zsh|dash|python
|
|
171
|
+
const INTERPRETER_FLAGS = /(?:^|[\\/\s])(?:bash|sh|zsh|dash|python[0-9.]*|pypy[0-9.]*|node(?:js)?|bun|deno|perl|ruby|php|lua)\s+(?:-c|-e|--command|--eval)\b/;
|
|
124
172
|
if (INTERPRETER_FLAGS.test(unquotedCommand)) {
|
|
125
173
|
return { allowed: false, reason: "Interpreter -c/-e invocation is blocked" };
|
|
126
174
|
}
|
|
127
|
-
//
|
|
175
|
+
// Многострочники прячут запрещённые команды после перевода строки.
|
|
128
176
|
if (/\r?\n/.test(trimmedCommand)) {
|
|
129
177
|
return { allowed: false, reason: "Multi-line commands are blocked" };
|
|
130
178
|
}
|
|
131
|
-
//
|
|
179
|
+
// Цепочки обходят проверку базовой команды ("echo hi; rm -rf /").
|
|
132
180
|
if (/[;&|]/.test(trimmedCommand) &&
|
|
133
181
|
/(^|[;&|\s])(rm|del|rd|dd|chmod|chown|wget|curl|nc|netcat|shutdown|reboot|mkfs|fdisk)\b/.test(trimmedCommand)) {
|
|
134
182
|
return { allowed: false, reason: "Command chaining to a dangerous command is blocked" };
|
|
135
183
|
}
|
|
136
|
-
//
|
|
137
|
-
//
|
|
184
|
+
// Опасные операторы — ПЕРЕД разбором базовой команды:
|
|
185
|
+
// "curl http://x | bash" ловится, даже если curl не в blacklist.
|
|
138
186
|
if (config.blockDangerousFlags) {
|
|
139
187
|
for (const op of config.dangerousOperators || []) {
|
|
140
188
|
if (containsOperator(trimmedCommand, op)) {
|
|
@@ -145,13 +193,13 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
145
193
|
}
|
|
146
194
|
}
|
|
147
195
|
}
|
|
148
|
-
//
|
|
196
|
+
// Базовая команда (пути, env, sudo уже учтены)
|
|
149
197
|
const baseCommand = extractBaseCommand(trimmedCommand);
|
|
150
|
-
//
|
|
151
|
-
// `RM -rf x`
|
|
198
|
+
// Сравнение регистронезависимо: exec на win32/macOS нечувствителен к регистру,
|
|
199
|
+
// `RM -rf x` обязан попасть в blacklist как `rm`.
|
|
152
200
|
const blacklistLower = new Set((config.blacklist || []).map((c) => c.toLowerCase()));
|
|
153
201
|
const whitelistLower = (config.whitelist || []).map((c) => c.toLowerCase());
|
|
154
|
-
//
|
|
202
|
+
// Whitelist сначала (непустой — разрешены только перечисленные)
|
|
155
203
|
if (whitelistLower.length > 0) {
|
|
156
204
|
if (!whitelistLower.includes(baseCommand.toLowerCase())) {
|
|
157
205
|
return {
|
|
@@ -160,14 +208,14 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
160
208
|
};
|
|
161
209
|
}
|
|
162
210
|
}
|
|
163
|
-
//
|
|
211
|
+
// Blacklist — по извлечённому basename, не по сырому токену
|
|
164
212
|
if (blacklistLower.has(baseCommand.toLowerCase())) {
|
|
165
213
|
return {
|
|
166
214
|
allowed: false,
|
|
167
215
|
reason: `Command "${baseCommand}" is blacklisted`,
|
|
168
216
|
};
|
|
169
217
|
}
|
|
170
|
-
//
|
|
218
|
+
// И сырой первый токен — вдруг это простое имя
|
|
171
219
|
const rawFirst = trimmedCommand
|
|
172
220
|
.split(/\s+/)[0]
|
|
173
221
|
.replace(/^["']+|["']+$/g, "")
|
|
@@ -178,10 +226,10 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
178
226
|
reason: `Command "${rawFirst}" is blacklisted`,
|
|
179
227
|
};
|
|
180
228
|
}
|
|
181
|
-
//
|
|
229
|
+
// Опасные флаги
|
|
182
230
|
if (config.blockDangerousFlags) {
|
|
183
231
|
for (const flag of config.dangerousFlags || []) {
|
|
184
|
-
//
|
|
232
|
+
// Флаг целым словом или с equals
|
|
185
233
|
const flagPattern = new RegExp(`(?:^|\\s)${flag}(?:\\s|$|=)`);
|
|
186
234
|
if (flagPattern.test(trimmedCommand)) {
|
|
187
235
|
return {
|
|
@@ -191,15 +239,15 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
191
239
|
}
|
|
192
240
|
}
|
|
193
241
|
}
|
|
194
|
-
//
|
|
242
|
+
// Команда разрешена
|
|
195
243
|
return { allowed: true, sanitizedCommand: command };
|
|
196
244
|
}
|
|
197
245
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
246
|
+
* Санитизация команды для логов (вырезание секретов).
|
|
247
|
+
* Те же паттерны, что в sanitizeLogMessage, — для консистентности.
|
|
200
248
|
*/
|
|
201
249
|
export function sanitizeCommandForLog(command) {
|
|
202
|
-
//
|
|
250
|
+
// Паттерны из data-sanitizer — для консистентности
|
|
203
251
|
const patterns = [
|
|
204
252
|
/(api[_-]?key|apikey|api_key)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
|
|
205
253
|
/(token|access[_-]?token|auth[_-]?token)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
|
|
@@ -215,9 +263,9 @@ export function sanitizeCommandForLog(command) {
|
|
|
215
263
|
];
|
|
216
264
|
let sanitized = command;
|
|
217
265
|
for (const pattern of patterns) {
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
266
|
+
// Замена целиком по каждому паттерну: замена с учётом групп
|
|
267
|
+
// переизлучила бы сам секрет для паттернов, где группа И ЕСТЬ значение
|
|
268
|
+
// (например, "Bearer <token>", sk-...).
|
|
221
269
|
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
|
222
270
|
}
|
|
223
271
|
return sanitized;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* them breaks ordinary CLI code (false positives).
|
|
2
|
+
* Дефолтные опасные паттерны.
|
|
3
|
+
* В списке только реально опасные конструкции. Легитимные рантайм-API
|
|
4
|
+
* (process.exit, setTimeout/setInterval, шаблонные литералы `${}`, бэктики)
|
|
5
|
+
* НЕ сканируются — это валидный JS/TS, их блок ломает обычный CLI-код.
|
|
7
6
|
*/
|
|
8
7
|
const DEFAULT_DANGEROUS_PATTERNS = [
|
|
9
8
|
/eval\(/gi,
|
|
@@ -24,14 +23,15 @@ const DEFAULT_DANGEROUS_PATTERNS = [
|
|
|
24
23
|
/curl.*\|.*sh/gi,
|
|
25
24
|
];
|
|
26
25
|
/**
|
|
27
|
-
*
|
|
26
|
+
* Проверить содержимое файла на опасные паттерны.
|
|
27
|
+
* Выключенный скан и пустой список паттернов разрешают всё.
|
|
28
28
|
*/
|
|
29
29
|
export function scanContent(content, filePath, config) {
|
|
30
|
-
//
|
|
30
|
+
// Выключенный скан разрешает всё
|
|
31
31
|
if (!config?.enabled) {
|
|
32
32
|
return { allowed: true };
|
|
33
33
|
}
|
|
34
|
-
//
|
|
34
|
+
// Без своих паттернов — дефолтные
|
|
35
35
|
const patterns = config.dangerousPatterns?.length
|
|
36
36
|
? config.dangerousPatterns
|
|
37
37
|
: DEFAULT_DANGEROUS_PATTERNS;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*/
|
|
1
|
+
// Вырезание секретов (ключи, токены, пароли) из логов, команд и URL.
|
|
2
|
+
/** Паттерны чувствительных данных для редактуры в логах. */
|
|
4
3
|
const SENSITIVE_PATTERNS = [
|
|
5
|
-
//
|
|
4
|
+
// Ключи и токены в формате key=value
|
|
6
5
|
{
|
|
7
6
|
pattern: /(api[_-]?key|apikey|api_key)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
|
|
8
7
|
replacement: "$1=[REDACTED]",
|
|
@@ -19,7 +18,7 @@ const SENSITIVE_PATTERNS = [
|
|
|
19
18
|
pattern: /(secret|private[_-]?key|secret[_-]?key)\s*[=:\s]\s*["']?([^\s"'&]+)["']?/gi,
|
|
20
19
|
replacement: "$1=[REDACTED]",
|
|
21
20
|
},
|
|
22
|
-
//
|
|
21
|
+
// Отдельные ключи (не key=value)
|
|
23
22
|
{
|
|
24
23
|
pattern: /\bsk-[a-zA-Z0-9_-]{20,}\b/gi,
|
|
25
24
|
replacement: "sk-[REDACTED]",
|
|
@@ -40,20 +39,18 @@ const SENSITIVE_PATTERNS = [
|
|
|
40
39
|
pattern: /\bAKIA[0-9A-Z]{15,}\b/gi,
|
|
41
40
|
replacement: "AKIA[REDACTED]",
|
|
42
41
|
},
|
|
43
|
-
// Bearer
|
|
42
|
+
// Bearer-токены в заголовках
|
|
44
43
|
{
|
|
45
44
|
pattern: /Bearer\s+[a-zA-Z0-9_-]{20,}/gi,
|
|
46
45
|
replacement: "Bearer [REDACTED]",
|
|
47
46
|
},
|
|
48
|
-
//
|
|
47
|
+
// Прочие высокоэнтропийные строки, похожие на секреты (30+ символов)
|
|
49
48
|
{
|
|
50
49
|
pattern: /\b([a-zA-Z0-9_-]{30,})\b/g,
|
|
51
50
|
replacement: "[REDACTED]",
|
|
52
51
|
},
|
|
53
52
|
];
|
|
54
|
-
/**
|
|
55
|
-
* Sanitize a log message by removing sensitive information
|
|
56
|
-
*/
|
|
53
|
+
/** Вычистить секреты из лог-сообщения. */
|
|
57
54
|
export function sanitizeLogMessage(message) {
|
|
58
55
|
let sanitized = message;
|
|
59
56
|
for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
|
|
@@ -61,15 +58,11 @@ export function sanitizeLogMessage(message) {
|
|
|
61
58
|
}
|
|
62
59
|
return sanitized;
|
|
63
60
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Sanitize a command string by removing sensitive information
|
|
66
|
-
*/
|
|
61
|
+
/** Вычистить секреты из командной строки. */
|
|
67
62
|
export function sanitizeCommand(command) {
|
|
68
63
|
return sanitizeLogMessage(command);
|
|
69
64
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Sanitize a URL by removing sensitive query parameters
|
|
72
|
-
*/
|
|
65
|
+
/** Вычистить секретные query-параметры из URL; не-URL — как лог-сообщение. */
|
|
73
66
|
export function sanitizeUrl(url) {
|
|
74
67
|
try {
|
|
75
68
|
const urlObj = new URL(url);
|
|
@@ -1,31 +1,26 @@
|
|
|
1
|
+
// Шифрование секретов конфига: AES-256-CBC + HMAC, ключ в ~/.mma/.encryption-key.
|
|
1
2
|
import { createCipheriv, createDecipheriv, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
2
|
-
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, copyFileSync } from "fs";
|
|
5
|
+
import { globalMmaDir } from "../../config/paths";
|
|
5
6
|
const DEFAULT_CONFIG = {
|
|
6
|
-
keyPath: join(
|
|
7
|
+
keyPath: join(globalMmaDir(), ".encryption-key"),
|
|
7
8
|
algorithm: "aes-256-gcm",
|
|
8
9
|
};
|
|
9
10
|
const IV_LENGTH = 16;
|
|
10
11
|
const SALT_LENGTH = 16;
|
|
11
12
|
const KEY_LENGTH = 32;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Generate a new encryption key
|
|
15
|
-
*/
|
|
13
|
+
/** Сгенерировать новый ключ шифрования. */
|
|
16
14
|
export function generateEncryptionKey() {
|
|
17
15
|
return randomBytes(KEY_LENGTH);
|
|
18
16
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Get or create encryption key from file
|
|
21
|
-
*/
|
|
17
|
+
/** Взять ключ из файла или создать новый. */
|
|
22
18
|
export function getOrCreateEncryptionKey(config) {
|
|
23
19
|
const { keyPath } = { ...DEFAULT_CONFIG, ...config };
|
|
24
|
-
//
|
|
20
|
+
// Каталог обязан существовать
|
|
25
21
|
const dir = join(keyPath, "..");
|
|
26
22
|
if (!existsSync(dir)) {
|
|
27
|
-
//
|
|
28
|
-
// This will be handled by the caller
|
|
23
|
+
// mkdirSync тут нельзя — чистая функция, создаёт вызывающий
|
|
29
24
|
}
|
|
30
25
|
if (!existsSync(keyPath)) {
|
|
31
26
|
const key = generateEncryptionKey();
|
|
@@ -34,8 +29,8 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
34
29
|
return key;
|
|
35
30
|
}
|
|
36
31
|
catch (e) {
|
|
37
|
-
//
|
|
38
|
-
//
|
|
32
|
+
// Без персиста ключа зашифрованное станет невосстановимым при
|
|
33
|
+
// следующем запуске — падаем громко, а не деградируем молча.
|
|
39
34
|
throw new Error(`Cannot write encryption key to ${keyPath}: ${errMsg(e)}`);
|
|
40
35
|
}
|
|
41
36
|
}
|
|
@@ -44,13 +39,13 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
44
39
|
return Buffer.from(keyBase64, "base64");
|
|
45
40
|
}
|
|
46
41
|
catch {
|
|
47
|
-
//
|
|
42
|
+
// Битый ключ бэкапим перед выбросом
|
|
48
43
|
try {
|
|
49
44
|
const backupPath = `${keyPath}.corrupted.${Date.now()}`;
|
|
50
45
|
copyFileSync(keyPath, backupPath);
|
|
51
46
|
}
|
|
52
47
|
catch {
|
|
53
|
-
/*
|
|
48
|
+
/* бэкап не удался, идём дальше */
|
|
54
49
|
}
|
|
55
50
|
throw new Error(`Encryption key file is corrupted or unreadable: ${keyPath}. ` +
|
|
56
51
|
`All previously encrypted data will be unrecoverable. ` +
|
|
@@ -58,32 +53,30 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
58
53
|
}
|
|
59
54
|
}
|
|
60
55
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
56
|
+
* Зашифровать строку (AES-256-GCM).
|
|
57
|
+
* В crypto Bun нет getAuthTag/setAuthTag напрямую, поэтому проще:
|
|
58
|
+
* AES-256-CBC + HMAC для аутентификации.
|
|
64
59
|
*/
|
|
65
60
|
export function encryptString(plaintext, key) {
|
|
66
61
|
const iv = randomBytes(IV_LENGTH);
|
|
67
62
|
const salt = randomBytes(SALT_LENGTH);
|
|
68
|
-
//
|
|
63
|
+
// Ключ шифрования и HMAC-ключ выводятся из главного ключа
|
|
69
64
|
const encKey = scryptSync(key, salt, KEY_LENGTH);
|
|
70
65
|
const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
|
|
71
66
|
const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
|
|
72
|
-
//
|
|
67
|
+
// Шифрование AES-256-CBC
|
|
73
68
|
const cipher = createCipheriv("aes-256-cbc", encKey, iv);
|
|
74
69
|
let encrypted = cipher.update(plaintext, "utf8", "base64");
|
|
75
70
|
encrypted += cipher.final("base64");
|
|
76
|
-
//
|
|
71
|
+
// HMAC для аутентификации
|
|
77
72
|
const hmac = createHmac("sha256", hmacKey);
|
|
78
73
|
hmac.update(iv);
|
|
79
74
|
hmac.update(Buffer.from(encrypted, "base64"));
|
|
80
75
|
const authTag = hmac.digest();
|
|
81
|
-
//
|
|
76
|
+
// Формат: salt:iv:authTag:encrypted
|
|
82
77
|
return `${salt.toString("base64")}:${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`;
|
|
83
78
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Decrypt a string using AES-256-CBC + HMAC
|
|
86
|
-
*/
|
|
79
|
+
/** Расшифровать строку (AES-256-CBC + HMAC). */
|
|
87
80
|
export function decryptString(encryptedText, key) {
|
|
88
81
|
const parts = encryptedText.split(":");
|
|
89
82
|
if (parts.length !== 4) {
|
|
@@ -93,11 +86,11 @@ export function decryptString(encryptedText, key) {
|
|
|
93
86
|
const iv = Buffer.from(parts[1], "base64");
|
|
94
87
|
const authTag = Buffer.from(parts[2], "base64");
|
|
95
88
|
const encrypted = parts[3];
|
|
96
|
-
//
|
|
89
|
+
// Ключ шифрования и HMAC-ключ выводятся из главного ключа
|
|
97
90
|
const encKey = scryptSync(key, salt, KEY_LENGTH);
|
|
98
91
|
const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
|
|
99
92
|
const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
|
|
100
|
-
//
|
|
93
|
+
// Проверка HMAC
|
|
101
94
|
const hmac = createHmac("sha256", hmacKey);
|
|
102
95
|
hmac.update(iv);
|
|
103
96
|
hmac.update(Buffer.from(encrypted, "base64"));
|
|
@@ -105,7 +98,7 @@ export function decryptString(encryptedText, key) {
|
|
|
105
98
|
if (computedTag.length !== authTag.length || !timingSafeEqual(computedTag, authTag)) {
|
|
106
99
|
throw new Error("Authentication failed: invalid tag");
|
|
107
100
|
}
|
|
108
|
-
//
|
|
101
|
+
// Расшифровка AES-256-CBC
|
|
109
102
|
const decipher = createDecipheriv("aes-256-cbc", encKey, iv);
|
|
110
103
|
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
111
104
|
decrypted += decipher.final("utf8");
|
|
@@ -113,23 +106,18 @@ export function decryptString(encryptedText, key) {
|
|
|
113
106
|
}
|
|
114
107
|
import { createHmac } from "node:crypto";
|
|
115
108
|
import { errMsg } from "../../utils";
|
|
116
|
-
/**
|
|
117
|
-
* Check if a string looks like it's encrypted
|
|
118
|
-
*/
|
|
109
|
+
/** Похоже ли значение на зашифрованное (формат salt:iv:authTag:encrypted). */
|
|
119
110
|
export function isEncryptedString(value) {
|
|
120
|
-
//
|
|
121
|
-
// Each part is base64 encoded and separated by colons
|
|
111
|
+
// Формат: salt:iv:authTag:encrypted, каждая часть — base64 через двоеточие
|
|
122
112
|
const parts = value.split(":");
|
|
123
113
|
if (parts.length !== 4)
|
|
124
114
|
return false;
|
|
125
|
-
//
|
|
126
|
-
// (
|
|
115
|
+
// Строгая проверка формы base64: Buffer.from(value, "base64") НИКОГДА не бросает
|
|
116
|
+
// (ленивое декодирование), так что plaintext от base64 он не отличит.
|
|
127
117
|
const isB64 = (s) => s.length > 0 && s.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(s);
|
|
128
118
|
return parts.every(isB64);
|
|
129
119
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Sensitive field names that should be encrypted
|
|
132
|
-
*/
|
|
120
|
+
/** Имена чувствительных полей для шифрования. */
|
|
133
121
|
export const SENSITIVE_FIELD_NAMES = [
|
|
134
122
|
"api_key",
|
|
135
123
|
"apikey",
|
|
@@ -147,16 +135,12 @@ export const SENSITIVE_FIELD_NAMES = [
|
|
|
147
135
|
"authorization",
|
|
148
136
|
"key",
|
|
149
137
|
];
|
|
150
|
-
/**
|
|
151
|
-
* Check if a field name indicates sensitive data
|
|
152
|
-
*/
|
|
138
|
+
/** Имя поля указывает на чувствительные данные или нет. */
|
|
153
139
|
export function isSensitiveFieldName(fieldName) {
|
|
154
140
|
const lowerFieldName = fieldName.toLowerCase();
|
|
155
141
|
return SENSITIVE_FIELD_NAMES.some((name) => lowerFieldName.includes(name));
|
|
156
142
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Encrypt sensitive fields in an object
|
|
159
|
-
*/
|
|
143
|
+
/** Зашифровать чувствительные поля объекта (рекурсивно, RegExp не трогает). */
|
|
160
144
|
export function encryptSensitiveFields(obj, key) {
|
|
161
145
|
const result = {};
|
|
162
146
|
for (const [fieldName, value] of Object.entries(obj)) {
|
|
@@ -182,9 +166,7 @@ export function encryptSensitiveFields(obj, key) {
|
|
|
182
166
|
}
|
|
183
167
|
return result;
|
|
184
168
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Decrypt sensitive fields in an object
|
|
187
|
-
*/
|
|
169
|
+
/** Расшифровать чувствительные поля объекта; неудача — оставить как есть. */
|
|
188
170
|
export function decryptSensitiveFields(obj, key) {
|
|
189
171
|
const result = {};
|
|
190
172
|
for (const [fieldName, value] of Object.entries(obj)) {
|
|
@@ -193,7 +175,7 @@ export function decryptSensitiveFields(obj, key) {
|
|
|
193
175
|
result[fieldName] = decryptString(value, key);
|
|
194
176
|
}
|
|
195
177
|
catch {
|
|
196
|
-
//
|
|
178
|
+
// Расшифровка не удалась — оставить зашифрованное значение
|
|
197
179
|
result[fieldName] = value;
|
|
198
180
|
}
|
|
199
181
|
}
|
|
@@ -216,9 +198,7 @@ export function decryptSensitiveFields(obj, key) {
|
|
|
216
198
|
}
|
|
217
199
|
return result;
|
|
218
200
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Create a wrapper for encrypting/decrypting config values
|
|
221
|
-
*/
|
|
201
|
+
/** Обёртка шифрования/расшифровки значений конфига. */
|
|
222
202
|
export class ConfigEncryptor {
|
|
223
203
|
key;
|
|
224
204
|
constructor(config) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Публичный фасад модуля безопасности: валидаторы, аудит, шифрование, политики.
|
|
1
2
|
export * from "./command-validator";
|
|
2
3
|
export * from "./path-validator";
|
|
3
4
|
export * from "./network-validator";
|
|
@@ -10,5 +11,5 @@ export * from "./session-isolation";
|
|
|
10
11
|
export * from "./session-encryption";
|
|
11
12
|
export * from "./audit-notifier";
|
|
12
13
|
export * from "./security-policies";
|
|
13
|
-
//
|
|
14
|
+
// Реэкспорт sanitizeUrl из network-validator — единый источник (без конфликтов)
|
|
14
15
|
export { sanitizeUrl } from "./network-validator";
|