@vyuhlabs/dxkit 2.4.8 → 2.5.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 +235 -0
- package/README.md +360 -439
- package/dist/analyzers/security/aggregator.d.ts.map +1 -1
- package/dist/analyzers/security/aggregator.js +4 -46
- package/dist/analyzers/security/aggregator.js.map +1 -1
- package/dist/analyzers/tools/fingerprint.d.ts +91 -26
- package/dist/analyzers/tools/fingerprint.d.ts.map +1 -1
- package/dist/analyzers/tools/fingerprint.js +111 -22
- package/dist/analyzers/tools/fingerprint.js.map +1 -1
- package/dist/analyzers/tools/generic.d.ts.map +1 -1
- package/dist/analyzers/tools/generic.js +6 -1
- package/dist/analyzers/tools/generic.js.map +1 -1
- package/dist/analyzers/tools/gitleaks.d.ts +24 -1
- package/dist/analyzers/tools/gitleaks.d.ts.map +1 -1
- package/dist/analyzers/tools/gitleaks.js +20 -11
- package/dist/analyzers/tools/gitleaks.js.map +1 -1
- package/dist/analyzers/types.d.ts +6 -4
- package/dist/analyzers/types.d.ts.map +1 -1
- package/dist/baseline/baseline-file.d.ts +104 -0
- package/dist/baseline/baseline-file.d.ts.map +1 -0
- package/dist/baseline/baseline-file.js +110 -0
- package/dist/baseline/baseline-file.js.map +1 -0
- package/dist/baseline/check-renderers.d.ts +108 -0
- package/dist/baseline/check-renderers.d.ts.map +1 -0
- package/dist/baseline/check-renderers.js +379 -0
- package/dist/baseline/check-renderers.js.map +1 -0
- package/dist/baseline/check.d.ts +127 -0
- package/dist/baseline/check.d.ts.map +1 -0
- package/dist/baseline/check.js +462 -0
- package/dist/baseline/check.js.map +1 -0
- package/dist/baseline/content-hash.d.ts +83 -0
- package/dist/baseline/content-hash.d.ts.map +1 -0
- package/dist/baseline/content-hash.js +131 -0
- package/dist/baseline/content-hash.js.map +1 -0
- package/dist/baseline/create.d.ts +96 -0
- package/dist/baseline/create.d.ts.map +1 -0
- package/dist/baseline/create.js +339 -0
- package/dist/baseline/create.js.map +1 -0
- package/dist/baseline/entry-to-located.d.ts +35 -0
- package/dist/baseline/entry-to-located.d.ts.map +1 -0
- package/dist/baseline/entry-to-located.js +72 -0
- package/dist/baseline/entry-to-located.js.map +1 -0
- package/dist/baseline/finding-identity.d.ts +47 -0
- package/dist/baseline/finding-identity.d.ts.map +1 -0
- package/dist/baseline/finding-identity.js +292 -0
- package/dist/baseline/finding-identity.js.map +1 -0
- package/dist/baseline/git-aware-match.d.ts +146 -0
- package/dist/baseline/git-aware-match.d.ts.map +1 -0
- package/dist/baseline/git-aware-match.js +439 -0
- package/dist/baseline/git-aware-match.js.map +1 -0
- package/dist/baseline/policy.d.ts +171 -0
- package/dist/baseline/policy.d.ts.map +1 -0
- package/dist/baseline/policy.js +206 -0
- package/dist/baseline/policy.js.map +1 -0
- package/dist/baseline/producers/health.d.ts +30 -0
- package/dist/baseline/producers/health.d.ts.map +1 -0
- package/dist/baseline/producers/health.js +42 -0
- package/dist/baseline/producers/health.js.map +1 -0
- package/dist/baseline/producers/index.d.ts +164 -0
- package/dist/baseline/producers/index.d.ts.map +1 -0
- package/dist/baseline/producers/index.js +200 -0
- package/dist/baseline/producers/index.js.map +1 -0
- package/dist/baseline/producers/licenses.d.ts +23 -0
- package/dist/baseline/producers/licenses.d.ts.map +1 -0
- package/dist/baseline/producers/licenses.js +46 -0
- package/dist/baseline/producers/licenses.js.map +1 -0
- package/dist/baseline/producers/quality.d.ts +39 -0
- package/dist/baseline/producers/quality.d.ts.map +1 -0
- package/dist/baseline/producers/quality.js +84 -0
- package/dist/baseline/producers/quality.js.map +1 -0
- package/dist/baseline/producers/secret-hmac.d.ts +45 -0
- package/dist/baseline/producers/secret-hmac.d.ts.map +1 -0
- package/dist/baseline/producers/secret-hmac.js +70 -0
- package/dist/baseline/producers/secret-hmac.js.map +1 -0
- package/dist/baseline/producers/security.d.ts +59 -0
- package/dist/baseline/producers/security.d.ts.map +1 -0
- package/dist/baseline/producers/security.js +135 -0
- package/dist/baseline/producers/security.js.map +1 -0
- package/dist/baseline/producers/tests.d.ts +36 -0
- package/dist/baseline/producers/tests.d.ts.map +1 -0
- package/dist/baseline/producers/tests.js +69 -0
- package/dist/baseline/producers/tests.js.map +1 -0
- package/dist/baseline/salt.d.ts +45 -0
- package/dist/baseline/salt.d.ts.map +1 -0
- package/dist/baseline/salt.js +113 -0
- package/dist/baseline/salt.js.map +1 -0
- package/dist/baseline/show.d.ts +79 -0
- package/dist/baseline/show.d.ts.map +1 -0
- package/dist/baseline/show.js +233 -0
- package/dist/baseline/show.js.map +1 -0
- package/dist/baseline/types.d.ts +482 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +53 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +360 -81
- package/dist/cli.js.map +1 -1
- package/dist/codebase-scanner.d.ts.map +1 -1
- package/dist/codebase-scanner.js +0 -1
- package/dist/codebase-scanner.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -4
- package/dist/constants.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +22 -25
- package/dist/doctor.js.map +1 -1
- package/dist/fail-on.d.ts +84 -0
- package/dist/fail-on.d.ts.map +1 -0
- package/dist/fail-on.js +128 -0
- package/dist/fail-on.js.map +1 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +2 -141
- package/dist/generator.js.map +1 -1
- package/dist/languages/csharp.d.ts.map +1 -1
- package/dist/languages/csharp.js +0 -9
- package/dist/languages/csharp.js.map +1 -1
- package/dist/languages/go.d.ts.map +1 -1
- package/dist/languages/go.js +0 -15
- package/dist/languages/go.js.map +1 -1
- package/dist/languages/index.d.ts +1 -1
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/java.d.ts.map +1 -1
- package/dist/languages/java.js +0 -6
- package/dist/languages/java.js.map +1 -1
- package/dist/languages/kotlin.d.ts.map +1 -1
- package/dist/languages/kotlin.js +0 -11
- package/dist/languages/kotlin.js.map +1 -1
- package/dist/languages/python.d.ts.map +1 -1
- package/dist/languages/python.js +0 -15
- package/dist/languages/python.js.map +1 -1
- package/dist/languages/ruby.d.ts.map +1 -1
- package/dist/languages/ruby.js +0 -6
- package/dist/languages/ruby.js.map +1 -1
- package/dist/languages/rust.d.ts.map +1 -1
- package/dist/languages/rust.js +0 -4
- package/dist/languages/rust.js.map +1 -1
- package/dist/languages/types.d.ts +2 -28
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +26 -4
- package/dist/languages/typescript.js.map +1 -1
- package/dist/lib.d.ts +2 -3
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +3 -6
- package/dist/lib.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +0 -10
- package/dist/prompts.js.map +1 -1
- package/dist/report-schema.d.ts +42 -0
- package/dist/report-schema.d.ts.map +1 -0
- package/dist/report-schema.js +54 -0
- package/dist/report-schema.js.map +1 -0
- package/dist/ship-installers.d.ts +106 -0
- package/dist/ship-installers.d.ts.map +1 -0
- package/dist/ship-installers.js +415 -0
- package/dist/ship-installers.js.map +1 -0
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +0 -4
- package/dist/update.js.map +1 -1
- package/package.json +17 -11
- package/templates/.claude/agents/onboarding.md +5 -4
- package/templates/.claude/agents-available/codebase-explorer.md +1 -1
- package/templates/.claude/agents-available/debugger.md +2 -2
- package/templates/.claude/agents-available/health-auditor.md +2 -2
- package/templates/.claude/commands/doctor.md +20 -12
- package/templates/.claude/skills/build/SKILL.md.template +22 -30
- package/templates/.claude/skills/deploy/SKILL.md.template +5 -25
- package/templates/.claude/skills/doctor/SKILL.md +24 -47
- package/templates/.claude/skills/gcloud/SKILL.md +5 -5
- package/templates/.claude/skills/learned/SKILL.md +1 -1
- package/templates/.claude/skills/pulumi/SKILL.md +2 -2
- package/templates/.claude/skills/quality/SKILL.md.template +4 -23
- package/templates/.claude/skills/review/SKILL.md.template +4 -3
- package/templates/.claude/skills/scaffold/SKILL.md.template +5 -15
- package/templates/.claude/skills/secrets/SKILL.md +20 -21
- package/templates/.claude/skills/session/SKILL.md +20 -31
- package/templates/.claude/skills/test/SKILL.md.template +1 -7
- package/templates/.devcontainer/devcontainer.json +81 -0
- package/templates/.devcontainer/install-agent-clis.sh +42 -0
- package/templates/.devcontainer/post-create.sh +67 -0
- package/templates/.githooks/pre-commit +55 -0
- package/templates/.githooks/pre-push +63 -0
- package/templates/.github/workflows/dxkit-baseline-refresh.yml +78 -0
- package/templates/.github/workflows/dxkit-guardrails.yml +98 -0
- package/templates/CLAUDE.md.template +62 -196
- package/dist/project-yaml.d.ts +0 -13
- package/dist/project-yaml.d.ts.map +0 -1
- package/dist/project-yaml.js +0 -188
- package/dist/project-yaml.js.map +0 -1
- package/templates/.ai/README.md +0 -117
- package/templates/.ai/prompts/execution-prompt.md +0 -9
- package/templates/.ai/prompts/planning-prompt.md +0 -18
- package/templates/.ai/prompts/session-end-template.md +0 -182
- package/templates/.ai/prompts/session-end.md +0 -132
- package/templates/.ai/prompts/session-start.md +0 -109
- package/templates/.ai/prompts/step-by-step.md +0 -113
- package/templates/.ai/sessions/.gitkeep +0 -0
- package/templates/.claude/commands/setup-pr-review.md +0 -72
- package/templates/.devcontainer/Dockerfile.dev.template +0 -89
- package/templates/.devcontainer/devcontainer.json.template +0 -184
- package/templates/.devcontainer/docker-compose.yml.template +0 -105
- package/templates/.devcontainer/init-scripts/01-init.sql.template +0 -12
- package/templates/.devcontainer/post-create.sh.template +0 -298
- package/templates/.github/workflows/ci.yml.template +0 -399
- package/templates/.github/workflows/quality.yml.template +0 -376
- package/templates/.pre-commit-config.yaml.template +0 -106
- package/templates/.project/config/edit_config.py +0 -275
- package/templates/.project/config/project_config.py +0 -894
- package/templates/.project/scripts/codegen/generate-all.sh +0 -20
- package/templates/.project/scripts/codegen/validate-all.sh +0 -17
- package/templates/.project/scripts/docs/generate-all.sh +0 -30
- package/templates/.project/scripts/docs/serve.sh +0 -20
- package/templates/.project/scripts/quality/fix-all.sh +0 -138
- package/templates/.project/scripts/quality/lint-go.sh +0 -34
- package/templates/.project/scripts/quality/lint-python.sh +0 -54
- package/templates/.project/scripts/quality/run-all.sh +0 -497
- package/templates/.project/scripts/session/commit.sh +0 -70
- package/templates/.project/scripts/session/create-pr.sh +0 -165
- package/templates/.project/scripts/session/end.sh +0 -207
- package/templates/.project/scripts/session/start.sh +0 -233
- package/templates/.project/scripts/setup/doctor.sh +0 -404
- package/templates/.project/scripts/setup/interactive-setup.sh +0 -585
- package/templates/.project/scripts/sync/sync-template.sh +0 -328
- package/templates/.project/scripts/test/run-all.sh +0 -179
- package/templates/.project/scripts/test/run-quick.sh +0 -25
- package/templates/Makefile +0 -514
- package/templates/config/versions.yaml +0 -57
- package/templates/configs/go/.golangci.yml.template +0 -172
- package/templates/configs/go/go.mod.template +0 -15
- package/templates/configs/java/README.md +0 -6
- package/templates/configs/kotlin/README.md +0 -6
- package/templates/configs/node/package.json.template +0 -67
- package/templates/configs/node/tsconfig.json.template +0 -53
- package/templates/configs/python/pyproject.toml.template +0 -92
- package/templates/configs/python/pytest.ini.template +0 -64
- package/templates/configs/python/ruff.toml.template +0 -79
- package/templates/configs/ruby/README.md +0 -6
- package/templates/configs/rust/Cargo.toml.template +0 -51
- package/templates/configs/shared/.editorconfig +0 -67
- package/templates/scripts/validate-templates.sh +0 -449
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `dxkit guardrail check` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* The matcher (`gitAwareMatch`) and classifier (`classify`) are pure
|
|
6
|
+
* modules that already exist. This file wires them together with the
|
|
7
|
+
* baseline file format, the producer pipeline, and the per-pair
|
|
8
|
+
* context lookups (severity, drift signals, changed-line overlap)
|
|
9
|
+
* the classifier needs to make policy decisions.
|
|
10
|
+
*
|
|
11
|
+
* Pipeline:
|
|
12
|
+
*
|
|
13
|
+
* 1. Load the prior baseline file.
|
|
14
|
+
* 2. Re-run every analyzer (via `gatherCurrentScan`) to produce the
|
|
15
|
+
* current side of the diff.
|
|
16
|
+
* 3. Convert both sides to `LocatedIdentity[]` and run the
|
|
17
|
+
* git-aware matcher.
|
|
18
|
+
* 4. Build per-pair classify context:
|
|
19
|
+
* - severity from the current security aggregate or per-kind
|
|
20
|
+
* defaults
|
|
21
|
+
* - kind from the matched BaselineEntry
|
|
22
|
+
* - scannerVersionDiffers from per-kind tool version compare
|
|
23
|
+
* - configDiffers from envelope hash compare
|
|
24
|
+
* - overlapsChangedLines from `git diff base..HEAD` hunks
|
|
25
|
+
* intersected with the finding's line
|
|
26
|
+
* 5. Run the brownfield policy classifier over every pair.
|
|
27
|
+
* 6. Optionally filter via `--changed-only`: drop pairs whose
|
|
28
|
+
* locator falls outside the diff. Non-locator pairs (dep-vuln,
|
|
29
|
+
* license, duplication, etc.) are always kept — their
|
|
30
|
+
* "semantic" identity doesn't map cleanly to changed lines.
|
|
31
|
+
* 7. Compose a `GuardrailCheckResult` with a deterministic
|
|
32
|
+
* blocks/warns verdict so the CLI can pick exit code + render.
|
|
33
|
+
*
|
|
34
|
+
* Drift signals come from comparing the baseline's `analysis` /
|
|
35
|
+
* `tools` envelope against the freshly-gathered envelope. Per-kind
|
|
36
|
+
* tool attribution uses the current run's `SecurityAggregate.provenance`
|
|
37
|
+
* — the cleaner alternative to a hardcoded kind→tool table.
|
|
38
|
+
*/
|
|
39
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
40
|
+
if (k2 === undefined) k2 = k;
|
|
41
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
42
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
43
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
44
|
+
}
|
|
45
|
+
Object.defineProperty(o, k2, desc);
|
|
46
|
+
}) : (function(o, m, k, k2) {
|
|
47
|
+
if (k2 === undefined) k2 = k;
|
|
48
|
+
o[k2] = m[k];
|
|
49
|
+
}));
|
|
50
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
51
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
52
|
+
}) : function(o, v) {
|
|
53
|
+
o["default"] = v;
|
|
54
|
+
});
|
|
55
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
56
|
+
var ownKeys = function(o) {
|
|
57
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
58
|
+
var ar = [];
|
|
59
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
60
|
+
return ar;
|
|
61
|
+
};
|
|
62
|
+
return ownKeys(o);
|
|
63
|
+
};
|
|
64
|
+
return function (mod) {
|
|
65
|
+
if (mod && mod.__esModule) return mod;
|
|
66
|
+
var result = {};
|
|
67
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
68
|
+
__setModuleDefault(result, mod);
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
})();
|
|
72
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
73
|
+
exports.runGuardrailCheck = runGuardrailCheck;
|
|
74
|
+
const child_process_1 = require("child_process");
|
|
75
|
+
const fs = __importStar(require("fs"));
|
|
76
|
+
const path = __importStar(require("path"));
|
|
77
|
+
const create_1 = require("./create");
|
|
78
|
+
const baseline_file_1 = require("./baseline-file");
|
|
79
|
+
const entry_to_located_1 = require("./entry-to-located");
|
|
80
|
+
const git_aware_match_1 = require("./git-aware-match");
|
|
81
|
+
const policy_1 = require("./policy");
|
|
82
|
+
const KIND_DEFAULT_SEVERITY = Object.freeze({
|
|
83
|
+
secret: 'high',
|
|
84
|
+
code: 'medium',
|
|
85
|
+
config: 'medium',
|
|
86
|
+
'dep-vuln': 'medium',
|
|
87
|
+
duplication: 'medium',
|
|
88
|
+
'coverage-gap': 'medium',
|
|
89
|
+
'test-gap': 'medium',
|
|
90
|
+
hygiene: 'low',
|
|
91
|
+
license: 'low',
|
|
92
|
+
'test-file-degradation': 'medium',
|
|
93
|
+
'god-file': 'medium',
|
|
94
|
+
'stale-file': 'low',
|
|
95
|
+
'large-file': 'medium',
|
|
96
|
+
'secret-hmac': 'high',
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* Run the guardrail-check pipeline. Pure-orchestrator: loads the
|
|
100
|
+
* baseline, gathers current state, runs the matcher + classifier,
|
|
101
|
+
* and returns a structured result. Renderers + CLI are downstream.
|
|
102
|
+
*/
|
|
103
|
+
async function runGuardrailCheck(options) {
|
|
104
|
+
const cwd = path.resolve(options.cwd);
|
|
105
|
+
const baselinePath = options.baselinePath ?? (0, baseline_file_1.pathForBaseline)(cwd, options.name ?? baseline_file_1.DEFAULT_BASELINE_NAME);
|
|
106
|
+
if (!fs.existsSync(baselinePath)) {
|
|
107
|
+
throw new Error(`baseline file not found: ${baselinePath}. ` +
|
|
108
|
+
`Run \`vyuh-dxkit baseline create\` first to capture today's state.`);
|
|
109
|
+
}
|
|
110
|
+
const baseline = (0, baseline_file_1.readBaselineFile)(baselinePath);
|
|
111
|
+
const policy = resolvePolicy(options.policyPath, cwd);
|
|
112
|
+
const current = await (0, create_1.gatherCurrentScan)({ cwd, verbose: options.verbose });
|
|
113
|
+
const priorLocated = (0, entry_to_located_1.entriesToLocated)(baseline.findings);
|
|
114
|
+
const currentLocated = (0, entry_to_located_1.entriesToLocated)(current.findings);
|
|
115
|
+
// The matcher needs the baseline's anchor commit to drive `git
|
|
116
|
+
// diff`. Empty string is the canonical "not a git repo at capture
|
|
117
|
+
// time" value; the matcher's reachability check handles it by
|
|
118
|
+
// falling back to plain set-diff (passes 1 + 1.5 are skipped).
|
|
119
|
+
const matchResult = (0, git_aware_match_1.gitAwareMatch)(priorLocated, currentLocated, {
|
|
120
|
+
cwd,
|
|
121
|
+
baseSha: baseline.repo.commitSha || 'HEAD',
|
|
122
|
+
headSha: 'HEAD',
|
|
123
|
+
});
|
|
124
|
+
const priorById = indexById(baseline.findings);
|
|
125
|
+
const currentById = indexById(current.findings);
|
|
126
|
+
const severityByCurrentId = buildSeverityIndex(current.aggregate);
|
|
127
|
+
const envelopeDrift = diffEnvelopes(baseline, current);
|
|
128
|
+
// Per-kind tool attribution drives the per-pair
|
|
129
|
+
// scannerVersionDiffers signal. A pair is in drift only when the
|
|
130
|
+
// tools that produced its kind actually changed version between
|
|
131
|
+
// runs — narrower than "any tool drifted globally," which would
|
|
132
|
+
// overstate the drift signal for unrelated kinds.
|
|
133
|
+
const toolsByKind = buildToolsByKind(current.aggregate);
|
|
134
|
+
const changedLineCache = new Map();
|
|
135
|
+
const headSha = readHeadSha(cwd);
|
|
136
|
+
const baseSha = baseline.repo.commitSha;
|
|
137
|
+
const linesChangedFor = (file) => {
|
|
138
|
+
if (!baseSha || !headSha)
|
|
139
|
+
return undefined;
|
|
140
|
+
let cached = changedLineCache.get(file);
|
|
141
|
+
if (cached)
|
|
142
|
+
return cached;
|
|
143
|
+
cached = readChangedLineSet(cwd, baseSha, headSha, file);
|
|
144
|
+
changedLineCache.set(file, cached);
|
|
145
|
+
return cached;
|
|
146
|
+
};
|
|
147
|
+
const classifiedPairs = [];
|
|
148
|
+
let blocks = false;
|
|
149
|
+
let warns = false;
|
|
150
|
+
for (const pair of matchResult.pairs) {
|
|
151
|
+
const anchorEntry = (pair.currentId ? currentById.get(pair.currentId) : undefined) ??
|
|
152
|
+
(pair.priorId ? priorById.get(pair.priorId) : undefined);
|
|
153
|
+
if (!anchorEntry)
|
|
154
|
+
continue;
|
|
155
|
+
const severity = (pair.currentId ? severityByCurrentId.get(pair.currentId) : undefined) ??
|
|
156
|
+
KIND_DEFAULT_SEVERITY[anchorEntry.kind];
|
|
157
|
+
const file = locatorFile(anchorEntry);
|
|
158
|
+
const line = locatorLine(anchorEntry);
|
|
159
|
+
const overlapsChangedLines = file !== undefined && line !== undefined && line > 0
|
|
160
|
+
? (linesChangedFor(file)?.has(line) ?? false)
|
|
161
|
+
: undefined;
|
|
162
|
+
const scannerVersionDiffers = pair.status === 'added' && kindHasDriftingTool(anchorEntry.kind, toolsByKind, envelopeDrift);
|
|
163
|
+
const configDiffers = pair.status === 'added' &&
|
|
164
|
+
(envelopeDrift.configHashChanged ||
|
|
165
|
+
envelopeDrift.ignoreHashChanged ||
|
|
166
|
+
envelopeDrift.policyHashChanged);
|
|
167
|
+
const context = {
|
|
168
|
+
severity,
|
|
169
|
+
kind: anchorEntry.kind,
|
|
170
|
+
...(scannerVersionDiffers ? { scannerVersionDiffers: true } : {}),
|
|
171
|
+
...(configDiffers ? { configDiffers: true } : {}),
|
|
172
|
+
...(overlapsChangedLines !== undefined ? { overlapsChangedLines } : {}),
|
|
173
|
+
};
|
|
174
|
+
const classification = (0, policy_1.classify)(pair, policy, context);
|
|
175
|
+
if (classification.blocks)
|
|
176
|
+
blocks = true;
|
|
177
|
+
if (classification.warns)
|
|
178
|
+
warns = true;
|
|
179
|
+
classifiedPairs.push({
|
|
180
|
+
pair,
|
|
181
|
+
classification,
|
|
182
|
+
severity,
|
|
183
|
+
kind: anchorEntry.kind,
|
|
184
|
+
...(file !== undefined ? { file } : {}),
|
|
185
|
+
...(line !== undefined ? { line } : {}),
|
|
186
|
+
...(overlapsChangedLines !== undefined ? { overlapsChangedLines } : {}),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const filteredPairs = options.changedOnly
|
|
190
|
+
? classifiedPairs.filter((p) => keepUnderChangedOnly(p))
|
|
191
|
+
: classifiedPairs;
|
|
192
|
+
// Re-derive the verdict after filtering — a --changed-only run
|
|
193
|
+
// shouldn't be blocked by a pair that the filter just dropped.
|
|
194
|
+
let filteredBlocks = false;
|
|
195
|
+
let filteredWarns = false;
|
|
196
|
+
for (const p of filteredPairs) {
|
|
197
|
+
if (p.classification.blocks)
|
|
198
|
+
filteredBlocks = true;
|
|
199
|
+
if (p.classification.warns)
|
|
200
|
+
filteredWarns = true;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
baselinePath,
|
|
204
|
+
baseline,
|
|
205
|
+
current,
|
|
206
|
+
matchResult,
|
|
207
|
+
pairs: filteredPairs,
|
|
208
|
+
envelopeDrift,
|
|
209
|
+
policy,
|
|
210
|
+
blocks: options.changedOnly ? filteredBlocks : blocks,
|
|
211
|
+
warns: options.changedOnly ? filteredWarns : warns,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/** Conventional location for a per-repo brownfield policy. Loaded
|
|
215
|
+
* automatically when present; can be overridden with `--policy`. */
|
|
216
|
+
const DEFAULT_POLICY_FILENAME = path.join('.dxkit', 'policy.json');
|
|
217
|
+
function resolvePolicy(policyPath, cwd) {
|
|
218
|
+
// Resolution order:
|
|
219
|
+
// 1. `--policy <path>` flag (explicit; errors if unreadable)
|
|
220
|
+
// 2. `<cwd>/.dxkit/policy.json` (conventional; silently skipped
|
|
221
|
+
// when absent so consumers without a policy use the defaults)
|
|
222
|
+
// 3. DEFAULT_BROWNFIELD_POLICY (compiled-in defaults)
|
|
223
|
+
let resolvedPath = policyPath;
|
|
224
|
+
if (!resolvedPath) {
|
|
225
|
+
const conventional = path.join(cwd, DEFAULT_POLICY_FILENAME);
|
|
226
|
+
if (fs.existsSync(conventional))
|
|
227
|
+
resolvedPath = conventional;
|
|
228
|
+
}
|
|
229
|
+
if (!resolvedPath)
|
|
230
|
+
return policy_1.DEFAULT_BROWNFIELD_POLICY;
|
|
231
|
+
let raw;
|
|
232
|
+
try {
|
|
233
|
+
raw = fs.readFileSync(resolvedPath, 'utf8');
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
throw new Error(`policy file not readable: ${resolvedPath} (${err.message})`);
|
|
237
|
+
}
|
|
238
|
+
let parsed;
|
|
239
|
+
try {
|
|
240
|
+
parsed = JSON.parse(raw);
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
throw new Error(`policy file is not valid JSON: ${resolvedPath} (${err.message})`);
|
|
244
|
+
}
|
|
245
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
246
|
+
throw new Error(`policy file root is not an object: ${resolvedPath}`);
|
|
247
|
+
}
|
|
248
|
+
// Shallow merge over the default. Per-field overrides win; unknown
|
|
249
|
+
// fields are preserved (the classifier reads only the fields it
|
|
250
|
+
// knows so unknowns are harmless).
|
|
251
|
+
const obj = parsed;
|
|
252
|
+
return {
|
|
253
|
+
...policy_1.DEFAULT_BROWNFIELD_POLICY,
|
|
254
|
+
...obj,
|
|
255
|
+
confidence: { ...policy_1.DEFAULT_BROWNFIELD_POLICY.confidence, ...(obj.confidence ?? {}) },
|
|
256
|
+
blockRules: { ...policy_1.DEFAULT_BROWNFIELD_POLICY.blockRules, ...(obj.blockRules ?? {}) },
|
|
257
|
+
block: obj.block ?? policy_1.DEFAULT_BROWNFIELD_POLICY.block,
|
|
258
|
+
warn: obj.warn ?? policy_1.DEFAULT_BROWNFIELD_POLICY.warn,
|
|
259
|
+
addedRequiresChangedLines: obj.addedRequiresChangedLines ?? policy_1.DEFAULT_BROWNFIELD_POLICY.addedRequiresChangedLines,
|
|
260
|
+
mode: 'brownfield',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function indexById(entries) {
|
|
264
|
+
const out = new Map();
|
|
265
|
+
for (const e of entries)
|
|
266
|
+
out.set(e.id, e);
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Severity-by-fingerprint index built from the current run's
|
|
271
|
+
* security aggregate. CodeFindings carry `fingerprint` (computed via
|
|
272
|
+
* `computeCodeFingerprint` — the same hash `identityFor` produces
|
|
273
|
+
* for secret/code/config kinds), and DepVulnFindings carry
|
|
274
|
+
* `fingerprint` (computed via `computeFingerprint` — same as
|
|
275
|
+
* identityFor for dep-vulns). For other kinds the lookup misses and
|
|
276
|
+
* the caller falls back to `KIND_DEFAULT_SEVERITY`.
|
|
277
|
+
*/
|
|
278
|
+
function buildSeverityIndex(aggregate) {
|
|
279
|
+
const out = new Map();
|
|
280
|
+
for (const f of aggregate.findingsByCategory.secret) {
|
|
281
|
+
if (f.fingerprint)
|
|
282
|
+
out.set(f.fingerprint, f.severity);
|
|
283
|
+
}
|
|
284
|
+
for (const f of aggregate.findingsByCategory.code) {
|
|
285
|
+
if (f.fingerprint)
|
|
286
|
+
out.set(f.fingerprint, f.severity);
|
|
287
|
+
}
|
|
288
|
+
for (const f of aggregate.findingsByCategory.config) {
|
|
289
|
+
if (f.fingerprint)
|
|
290
|
+
out.set(f.fingerprint, f.severity);
|
|
291
|
+
}
|
|
292
|
+
for (const f of aggregate.findingsByCategory.dependency) {
|
|
293
|
+
if (f.fingerprint)
|
|
294
|
+
out.set(f.fingerprint, f.severity);
|
|
295
|
+
}
|
|
296
|
+
return out;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Build a per-kind map of "tools that produced this kind in the
|
|
300
|
+
* current run." Used by the `scannerVersionDiffers` per-pair
|
|
301
|
+
* computation: a pair is in tool drift only when one of the tools
|
|
302
|
+
* that produced its kind has actually drifted version.
|
|
303
|
+
*/
|
|
304
|
+
function buildToolsByKind(aggregate) {
|
|
305
|
+
const secretTool = aggregate.provenance.secrets.tool ?? undefined;
|
|
306
|
+
const codeTool = aggregate.provenance.codePatterns.tool ?? undefined;
|
|
307
|
+
const depTool = aggregate.provenance.depVulns.tool ?? undefined;
|
|
308
|
+
const tlsBypassRan = aggregate.provenance.tlsBypass.ran;
|
|
309
|
+
const codeTools = new Set();
|
|
310
|
+
if (codeTool)
|
|
311
|
+
codeTools.add(codeTool);
|
|
312
|
+
if (tlsBypassRan)
|
|
313
|
+
codeTools.add('tls-bypass-registry');
|
|
314
|
+
const secretTools = new Set();
|
|
315
|
+
if (secretTool)
|
|
316
|
+
secretTools.add(secretTool);
|
|
317
|
+
const depTools = new Set();
|
|
318
|
+
if (depTool)
|
|
319
|
+
depTools.add(depTool);
|
|
320
|
+
return {
|
|
321
|
+
secret: secretTools,
|
|
322
|
+
code: codeTools,
|
|
323
|
+
config: secretTools, // .env-in-git + private-key files come from the secrets/file pass
|
|
324
|
+
'dep-vuln': depTools,
|
|
325
|
+
'secret-hmac': secretTools,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function kindHasDriftingTool(kind, toolsByKind, drift) {
|
|
329
|
+
const tools = toolsByKind[kind];
|
|
330
|
+
if (!tools || tools.size === 0)
|
|
331
|
+
return false;
|
|
332
|
+
for (const diff of drift.toolVersionDiffs) {
|
|
333
|
+
if (tools.has(diff.tool))
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
function diffEnvelopes(baseline, current) {
|
|
339
|
+
const toolVersionDiffs = [];
|
|
340
|
+
const names = new Set([...Object.keys(baseline.tools), ...Object.keys(current.tools)]);
|
|
341
|
+
for (const tool of [...names].sort()) {
|
|
342
|
+
const baselineVersion = baseline.tools[tool];
|
|
343
|
+
const currentVersion = current.tools[tool];
|
|
344
|
+
if (baselineVersion !== currentVersion) {
|
|
345
|
+
toolVersionDiffs.push({ tool, baselineVersion, currentVersion });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
toolchainHashChanged: baseline.analysis.toolchainHash !== current.analysisMeta.toolchainHash,
|
|
350
|
+
policyHashChanged: baseline.analysis.policyHash !== current.analysisMeta.policyHash,
|
|
351
|
+
ignoreHashChanged: baseline.analysis.ignoreHash !== current.analysisMeta.ignoreHash,
|
|
352
|
+
configHashChanged: baseline.analysis.configHash !== current.analysisMeta.configHash,
|
|
353
|
+
dxkitVersionChanged: baseline.analysis.dxkitVersion !== current.analysisMeta.dxkitVersion,
|
|
354
|
+
toolVersionDiffs,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function locatorFile(entry) {
|
|
358
|
+
switch (entry.kind) {
|
|
359
|
+
case 'secret':
|
|
360
|
+
case 'code':
|
|
361
|
+
case 'config':
|
|
362
|
+
case 'hygiene':
|
|
363
|
+
case 'test-gap':
|
|
364
|
+
case 'test-file-degradation':
|
|
365
|
+
case 'god-file':
|
|
366
|
+
case 'stale-file':
|
|
367
|
+
case 'large-file':
|
|
368
|
+
return entry.file;
|
|
369
|
+
case 'coverage-gap':
|
|
370
|
+
return entry.file;
|
|
371
|
+
case 'duplication':
|
|
372
|
+
return entry.fileA;
|
|
373
|
+
case 'dep-vuln':
|
|
374
|
+
case 'license':
|
|
375
|
+
case 'secret-hmac':
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function locatorLine(entry) {
|
|
380
|
+
switch (entry.kind) {
|
|
381
|
+
case 'secret':
|
|
382
|
+
case 'code':
|
|
383
|
+
case 'config':
|
|
384
|
+
case 'hygiene':
|
|
385
|
+
return entry.line;
|
|
386
|
+
case 'duplication':
|
|
387
|
+
return entry.startLineA;
|
|
388
|
+
case 'coverage-gap':
|
|
389
|
+
return entry.lineRange?.[0];
|
|
390
|
+
default:
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* `--changed-only` filter predicate. Keeps:
|
|
396
|
+
* - pairs without a line locator (dep-vuln, license, duplication,
|
|
397
|
+
* etc.) — their identity isn't line-bound, so changed-line
|
|
398
|
+
* overlap doesn't apply
|
|
399
|
+
* - prior-side pairs (persisted / relocated / removed) — they
|
|
400
|
+
* represent existing state, not newly-introduced findings, so
|
|
401
|
+
* they pass regardless of where they live in the diff
|
|
402
|
+
* - new-side pairs whose anchor line is inside the diff
|
|
403
|
+
*
|
|
404
|
+
* Drops new-side pairs (added / tooling_drift / config_drift /
|
|
405
|
+
* newly_detected) whose locator IS known but doesn't overlap any
|
|
406
|
+
* changed line. That's the exact scope a pre-commit / pre-push hook
|
|
407
|
+
* wants — "only flag what this developer just touched."
|
|
408
|
+
*/
|
|
409
|
+
function keepUnderChangedOnly(p) {
|
|
410
|
+
if (p.file === undefined || p.line === undefined)
|
|
411
|
+
return true;
|
|
412
|
+
const isNewSide = p.classification.status === 'added' ||
|
|
413
|
+
p.classification.status === 'tooling_drift' ||
|
|
414
|
+
p.classification.status === 'config_drift' ||
|
|
415
|
+
p.classification.status === 'newly_detected';
|
|
416
|
+
if (!isNewSide)
|
|
417
|
+
return true;
|
|
418
|
+
return p.overlapsChangedLines === true;
|
|
419
|
+
}
|
|
420
|
+
function readHeadSha(cwd) {
|
|
421
|
+
try {
|
|
422
|
+
return (0, child_process_1.execFileSync)('git', ['rev-parse', 'HEAD'], { cwd, encoding: 'utf8' }).trim();
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return '';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Compute the set of HEAD-side line numbers modified between
|
|
430
|
+
* `baseSha` and `headSha` for `file`. Used by the per-pair
|
|
431
|
+
* `overlapsChangedLines` signal: a current-side finding at line N
|
|
432
|
+
* overlaps the diff iff N is in this set.
|
|
433
|
+
*
|
|
434
|
+
* Walks `git diff --unified=0` hunks. Returns an empty set on any
|
|
435
|
+
* failure (file missing in either revision, git unavailable, etc.).
|
|
436
|
+
*/
|
|
437
|
+
function readChangedLineSet(cwd, baseSha, headSha, file) {
|
|
438
|
+
const out = new Set();
|
|
439
|
+
let diff;
|
|
440
|
+
try {
|
|
441
|
+
diff = (0, child_process_1.execFileSync)('git', ['diff', '--unified=0', '--no-color', '--find-renames', baseSha, headSha, '--', file], { cwd, encoding: 'utf8' });
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return out;
|
|
445
|
+
}
|
|
446
|
+
if (!diff.trim())
|
|
447
|
+
return out;
|
|
448
|
+
const hunkRe = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/gm;
|
|
449
|
+
let match;
|
|
450
|
+
while ((match = hunkRe.exec(diff)) !== null) {
|
|
451
|
+
const newStart = parseInt(match[1], 10);
|
|
452
|
+
const newCount = match[2] !== undefined ? parseInt(match[2], 10) : 1;
|
|
453
|
+
if (newCount === 0) {
|
|
454
|
+
// Pure-deletion hunk on the new side — no new-side lines.
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
for (let i = 0; i < newCount; i++)
|
|
458
|
+
out.add(newStart + i);
|
|
459
|
+
}
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/baseline/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-hash identity — drift-tolerant matching via the actual
|
|
3
|
+
* source code around a finding rather than its line position.
|
|
4
|
+
*
|
|
5
|
+
* The line-bucket identity scheme tolerates ±2 lines of drift; the
|
|
6
|
+
* git-aware matcher tolerates any line shift git can describe.
|
|
7
|
+
* Neither helps when git history is unavailable (shallow clone,
|
|
8
|
+
* force-pushed baseline commit) or when the line-bucket boundary
|
|
9
|
+
* straddles a context-stable region. The content-hash layer is the
|
|
10
|
+
* third fallback: pair findings whose surrounding code is byte-
|
|
11
|
+
* identical, regardless of where in the file they live.
|
|
12
|
+
*
|
|
13
|
+
* Pipeline:
|
|
14
|
+
*
|
|
15
|
+
* 1. The producer (Phase 3 baseline-create) reads each finding's
|
|
16
|
+
* surrounding context lines, normalizes whitespace, and
|
|
17
|
+
* computes a SHA-1[0:16] hash. The hash is stamped on the
|
|
18
|
+
* finding entry in the baseline file.
|
|
19
|
+
* 2. At guardrail-check time, the current scan computes content
|
|
20
|
+
* hashes the same way for its own findings.
|
|
21
|
+
* 3. The matcher's content-hash pass pairs prior + current
|
|
22
|
+
* findings with matching `(canonical-rule, contentHash)` after
|
|
23
|
+
* location-based pairing has exhausted what git can do.
|
|
24
|
+
*
|
|
25
|
+
* Trade-offs vs. line-bucket / git-aware match:
|
|
26
|
+
*
|
|
27
|
+
* - Survives any vertical drift (the line number is irrelevant).
|
|
28
|
+
* - Survives file rename + reformat + cross-file refactor when
|
|
29
|
+
* the immediate context survives.
|
|
30
|
+
* - Fails when the surrounding context changes — even a single
|
|
31
|
+
* adjacent variable rename invalidates the hash.
|
|
32
|
+
* - Vulnerable to collisions when two findings have identical
|
|
33
|
+
* context (rare for code-pattern findings, more likely for
|
|
34
|
+
* hygiene markers in similar boilerplate).
|
|
35
|
+
*
|
|
36
|
+
* The matcher tags content-hash matches with confidence 0.80 — below
|
|
37
|
+
* git-line-fuzz (0.88) so the brownfield policy's per-severity
|
|
38
|
+
* thresholds naturally distinguish them. For low-severity findings
|
|
39
|
+
* (threshold 0.90 by default), a content-hash match demotes to
|
|
40
|
+
* `'uncertain'` rather than silently pairing; for critical findings
|
|
41
|
+
* (threshold 0.75), the same match passes through cleanly.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Width of the context window read on each side of the finding's
|
|
45
|
+
* reported line. Three lines above + three lines below + the line
|
|
46
|
+
* itself = a seven-line window that captures the immediate
|
|
47
|
+
* surrounding code without being so wide that unrelated edits
|
|
48
|
+
* invalidate the hash.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CONTENT_HASH_CONTEXT_LINES = 3;
|
|
51
|
+
/**
|
|
52
|
+
* Pure function: compute the content hash for a finding at `line`
|
|
53
|
+
* inside `fileContent`. Whitespace is normalized — trailing
|
|
54
|
+
* whitespace stripped, internal runs collapsed to a single space,
|
|
55
|
+
* empty lines preserved as empty — so reformat-only edits don't
|
|
56
|
+
* churn the hash.
|
|
57
|
+
*
|
|
58
|
+
* `line` is 1-based to match every other dxkit line-number contract.
|
|
59
|
+
* `contextLines` defaults to `CONTENT_HASH_CONTEXT_LINES`. Lines
|
|
60
|
+
* before the start of the file or past the end are clamped (the
|
|
61
|
+
* window is smaller near the file edges; that's fine — hashing a
|
|
62
|
+
* shorter window is still deterministic).
|
|
63
|
+
*/
|
|
64
|
+
export declare function computeContentHash(fileContent: string, line: number, contextLines?: number): string;
|
|
65
|
+
/**
|
|
66
|
+
* Read a file's content at a specific commit. Returns null when git
|
|
67
|
+
* can't resolve the path-at-commit pair — file didn't exist at
|
|
68
|
+
* `sha`, file is binary, sha is unreachable. Callers treat null
|
|
69
|
+
* the same as "content-hash unavailable for this path."
|
|
70
|
+
*
|
|
71
|
+
* Uses `git show <sha>:<path>` which does not require checking out
|
|
72
|
+
* the commit — safe to call repeatedly in a tight loop without
|
|
73
|
+
* touching the working tree.
|
|
74
|
+
*/
|
|
75
|
+
export declare function readFileFromCommit(cwd: string, sha: string, file: string): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Combined helper: read the file at a commit and compute its
|
|
78
|
+
* content hash at the given line. Returns null when the file
|
|
79
|
+
* couldn't be read. Used by the producer (Phase 3 baseline-create)
|
|
80
|
+
* to stamp content hashes on baseline entries.
|
|
81
|
+
*/
|
|
82
|
+
export declare function computeContentHashFromCommit(cwd: string, sha: string, file: string, line: number, contextLines?: number): string | null;
|
|
83
|
+
//# sourceMappingURL=content-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-hash.d.ts","sourceRoot":"","sources":["../../src/baseline/content-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAKH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,MAAmC,GAChD,MAAM,CAOR;AAaD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBxF;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,MAAmC,GAChD,MAAM,GAAG,IAAI,CAIf"}
|