@vyuhlabs/dxkit 2.9.4 → 2.11.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 +236 -0
- package/dist/allowlist/annotate.d.ts +71 -0
- package/dist/allowlist/annotate.d.ts.map +1 -0
- package/dist/allowlist/annotate.js +105 -0
- package/dist/allowlist/annotate.js.map +1 -0
- package/dist/allowlist/cli.d.ts +29 -23
- package/dist/allowlist/cli.d.ts.map +1 -1
- package/dist/allowlist/cli.js +141 -70
- package/dist/allowlist/cli.js.map +1 -1
- package/dist/allowlist/file.d.ts +7 -1
- package/dist/allowlist/file.d.ts.map +1 -1
- package/dist/allowlist/file.js +7 -1
- package/dist/allowlist/file.js.map +1 -1
- package/dist/analysis-result.d.ts +10 -0
- package/dist/analysis-result.d.ts.map +1 -1
- package/dist/analyzers/cache.d.ts +1 -0
- package/dist/analyzers/cache.d.ts.map +1 -1
- package/dist/analyzers/cache.js +69 -0
- package/dist/analyzers/cache.js.map +1 -1
- package/dist/analyzers/dashboard/index.d.ts.map +1 -1
- package/dist/analyzers/dashboard/index.js +6 -1
- package/dist/analyzers/dashboard/index.js.map +1 -1
- package/dist/analyzers/health.d.ts.map +1 -1
- package/dist/analyzers/health.js +17 -2
- package/dist/analyzers/health.js.map +1 -1
- package/dist/analyzers/security/actions.d.ts.map +1 -1
- package/dist/analyzers/security/actions.js +13 -0
- package/dist/analyzers/security/actions.js.map +1 -1
- package/dist/analyzers/security/aggregator.d.ts +97 -79
- package/dist/analyzers/security/aggregator.d.ts.map +1 -1
- package/dist/analyzers/security/aggregator.js +168 -56
- package/dist/analyzers/security/aggregator.js.map +1 -1
- package/dist/analyzers/security/gather.d.ts +2 -0
- package/dist/analyzers/security/gather.d.ts.map +1 -1
- package/dist/analyzers/security/gather.js +36 -4
- package/dist/analyzers/security/gather.js.map +1 -1
- package/dist/analyzers/security/index.d.ts.map +1 -1
- package/dist/analyzers/security/index.js +81 -2
- package/dist/analyzers/security/index.js.map +1 -1
- package/dist/analyzers/security/scanner-drift.d.ts +21 -0
- package/dist/analyzers/security/scanner-drift.d.ts.map +1 -0
- package/dist/analyzers/security/scanner-drift.js +113 -0
- package/dist/analyzers/security/scanner-drift.js.map +1 -0
- package/dist/analyzers/security/shallow.d.ts.map +1 -1
- package/dist/analyzers/security/shallow.js +24 -2
- package/dist/analyzers/security/shallow.js.map +1 -1
- package/dist/analyzers/security/types.d.ts +64 -4
- package/dist/analyzers/security/types.d.ts.map +1 -1
- package/dist/analyzers/tools/fingerprint.d.ts +133 -20
- package/dist/analyzers/tools/fingerprint.d.ts.map +1 -1
- package/dist/analyzers/tools/fingerprint.js +194 -20
- package/dist/analyzers/tools/fingerprint.js.map +1 -1
- package/dist/analyzers/tools/gitleaks.d.ts +2 -2
- package/dist/analyzers/tools/gitleaks.d.ts.map +1 -1
- package/dist/analyzers/tools/gitleaks.js +7 -1
- package/dist/analyzers/tools/gitleaks.js.map +1 -1
- package/dist/analyzers/tools/graphify.d.ts +11 -0
- package/dist/analyzers/tools/graphify.d.ts.map +1 -1
- package/dist/analyzers/tools/graphify.js +457 -413
- package/dist/analyzers/tools/graphify.js.map +1 -1
- package/dist/analyzers/tools/grep-secrets.d.ts.map +1 -1
- package/dist/analyzers/tools/grep-secrets.js +31 -12
- package/dist/analyzers/tools/grep-secrets.js.map +1 -1
- package/dist/analyzers/tools/osv-scanner-fix.d.ts.map +1 -1
- package/dist/analyzers/tools/osv-scanner-fix.js +12 -1
- package/dist/analyzers/tools/osv-scanner-fix.js.map +1 -1
- package/dist/analyzers/tools/salt.d.ts +68 -0
- package/dist/analyzers/tools/salt.d.ts.map +1 -0
- package/dist/{baseline → analyzers/tools}/salt.js +59 -18
- package/dist/analyzers/tools/salt.js.map +1 -0
- package/dist/analyzers/tools/semgrep.d.ts +7 -7
- package/dist/analyzers/tools/semgrep.d.ts.map +1 -1
- package/dist/analyzers/tools/semgrep.js +14 -7
- package/dist/analyzers/tools/semgrep.js.map +1 -1
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
- package/dist/analyzers/tools/tool-registry.js +78 -43
- package/dist/analyzers/tools/tool-registry.js.map +1 -1
- package/dist/analyzers/tools/walk-source-files.d.ts +10 -0
- package/dist/analyzers/tools/walk-source-files.d.ts.map +1 -1
- package/dist/analyzers/tools/walk-source-files.js +14 -0
- package/dist/analyzers/tools/walk-source-files.js.map +1 -1
- package/dist/analyzers/types.d.ts +9 -0
- package/dist/analyzers/types.d.ts.map +1 -1
- package/dist/baseline/baseline-file.d.ts +9 -2
- package/dist/baseline/baseline-file.d.ts.map +1 -1
- package/dist/baseline/baseline-file.js.map +1 -1
- package/dist/baseline/check-renderers.d.ts.map +1 -1
- package/dist/baseline/check-renderers.js +14 -0
- package/dist/baseline/check-renderers.js.map +1 -1
- package/dist/baseline/check.d.ts +33 -0
- package/dist/baseline/check.d.ts.map +1 -1
- package/dist/baseline/check.js +78 -2
- package/dist/baseline/check.js.map +1 -1
- package/dist/baseline/create.d.ts +1 -1
- package/dist/baseline/create.d.ts.map +1 -1
- package/dist/baseline/create.js +3 -1
- package/dist/baseline/create.js.map +1 -1
- package/dist/baseline/entry-to-located.d.ts +12 -5
- package/dist/baseline/entry-to-located.d.ts.map +1 -1
- package/dist/baseline/entry-to-located.js +21 -7
- package/dist/baseline/entry-to-located.js.map +1 -1
- package/dist/baseline/finding-identity.d.ts +20 -13
- package/dist/baseline/finding-identity.d.ts.map +1 -1
- package/dist/baseline/finding-identity.js +51 -20
- package/dist/baseline/finding-identity.js.map +1 -1
- package/dist/baseline/git-aware-match.d.ts +7 -5
- package/dist/baseline/git-aware-match.d.ts.map +1 -1
- package/dist/baseline/git-aware-match.js +78 -5
- package/dist/baseline/git-aware-match.js.map +1 -1
- package/dist/baseline/migrate.d.ts +94 -0
- package/dist/baseline/migrate.d.ts.map +1 -0
- package/dist/baseline/migrate.js +238 -0
- package/dist/baseline/migrate.js.map +1 -0
- package/dist/baseline/producers/security.d.ts +9 -9
- package/dist/baseline/producers/security.d.ts.map +1 -1
- package/dist/baseline/producers/security.js +16 -4
- package/dist/baseline/producers/security.js.map +1 -1
- package/dist/baseline/types.d.ts +145 -95
- package/dist/baseline/types.d.ts.map +1 -1
- package/dist/baseline/types.js +30 -26
- package/dist/baseline/types.js.map +1 -1
- package/dist/explore/context-hook.d.ts +49 -29
- package/dist/explore/context-hook.d.ts.map +1 -1
- package/dist/explore/context-hook.js +304 -29
- package/dist/explore/context-hook.js.map +1 -1
- package/dist/explore/finding-context.d.ts +17 -0
- package/dist/explore/finding-context.d.ts.map +1 -1
- package/dist/explore/finding-context.js +34 -0
- package/dist/explore/finding-context.js.map +1 -1
- package/dist/explore/queries.d.ts +32 -15
- package/dist/explore/queries.d.ts.map +1 -1
- package/dist/explore/queries.js +36 -6
- package/dist/explore/queries.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +13 -7
- package/dist/generator.js.map +1 -1
- package/dist/ingest/normalize.d.ts +1 -1
- package/dist/ingest/normalize.d.ts.map +1 -1
- package/dist/ingest/normalize.js +5 -1
- package/dist/ingest/normalize.js.map +1 -1
- package/dist/ingest/sarif.d.ts.map +1 -1
- package/dist/ingest/sarif.js +16 -7
- package/dist/ingest/sarif.js.map +1 -1
- package/dist/ingest/snyk-policy.d.ts +22 -1
- package/dist/ingest/snyk-policy.d.ts.map +1 -1
- package/dist/ingest/snyk-policy.js +75 -18
- package/dist/ingest/snyk-policy.js.map +1 -1
- package/dist/ingest/types.d.ts +23 -12
- package/dist/ingest/types.d.ts.map +1 -1
- package/dist/languages/capabilities/types.d.ts +64 -53
- package/dist/languages/capabilities/types.d.ts.map +1 -1
- package/dist/languages/capabilities/types.js +4 -4
- package/dist/languages/index.d.ts +28 -5
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +38 -7
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +19 -0
- package/dist/languages/typescript.js.map +1 -1
- package/dist/scoring/dimensions/security.d.ts +17 -0
- package/dist/scoring/dimensions/security.d.ts.map +1 -1
- package/dist/scoring/dimensions/security.js +12 -0
- package/dist/scoring/dimensions/security.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +49 -0
- package/dist/update.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +2 -1
- package/dist/upgrade.js.map +1 -1
- package/package.json +6 -3
- package/templates/.claude/skills/dxkit-action/SKILL.md +11 -2
- package/templates/.claude/skills/dxkit-allowlist/SKILL.md +9 -0
- package/templates/.claude/skills/dxkit-onboard/SKILL.md +2 -2
- package/templates/.claude/skills/dxkit-update/SKILL.md +45 -4
- package/dist/baseline/salt.d.ts +0 -45
- package/dist/baseline/salt.d.ts.map +0 -1
- package/dist/baseline/salt.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectScannerCoverageDrift = detectScannerCoverageDrift;
|
|
37
|
+
/**
|
|
38
|
+
* Scanner-coverage drift detection across runs.
|
|
39
|
+
*
|
|
40
|
+
* Closes the "my score got worse after fixing things" confusion: when
|
|
41
|
+
* the active scanner set grows between two runs (e.g. `gitleaks` →
|
|
42
|
+
* `gitleaks, grep-secrets` + `snyk-code`), the newly-added scanners
|
|
43
|
+
* surface pre-existing findings that older runs simply couldn't see. The
|
|
44
|
+
* Security score can drop on an UNCHANGED commit, with nothing in the
|
|
45
|
+
* report explaining that those findings are newly *visible*, not newly
|
|
46
|
+
* *introduced*.
|
|
47
|
+
*
|
|
48
|
+
* This module compares the current run's scanner set against the most
|
|
49
|
+
* recent prior vulnerability-scan report persisted under
|
|
50
|
+
* `.dxkit/reports/`. When the current run added scanners, the report
|
|
51
|
+
* renders an honest note so a reader attributes any movement to improved
|
|
52
|
+
* measurement rather than regressed code.
|
|
53
|
+
*
|
|
54
|
+
* Read-only + fail-open: an fs or parse problem yields `null` (no note),
|
|
55
|
+
* never an error — drift disclosure is a nicety, not a gate.
|
|
56
|
+
*/
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const PREFIX = 'vulnerability-scan-';
|
|
60
|
+
const SUFFIX = '-detailed.json';
|
|
61
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
62
|
+
/**
|
|
63
|
+
* Detect scanners added since the most recent prior vuln-scan report.
|
|
64
|
+
* Returns `null` when there is no prior report, none is readable, or the
|
|
65
|
+
* scanner set did not grow (a shrinking or identical set is not the
|
|
66
|
+
* confusing case this note addresses).
|
|
67
|
+
*
|
|
68
|
+
* `currentDate` is this run's report date (YYYY-MM-DD); prior reports on
|
|
69
|
+
* a strictly earlier date are eligible. Same-day files are skipped: a
|
|
70
|
+
* report dated today is either this run's own prior invocation (already
|
|
71
|
+
* the post-expansion set — comparing would hide the drift) or about to
|
|
72
|
+
* be overwritten, so an earlier day is the honest comparison point.
|
|
73
|
+
*/
|
|
74
|
+
function detectScannerCoverageDrift(repoPath, currentTools, currentDate) {
|
|
75
|
+
const reportDir = path.join(repoPath, '.dxkit', 'reports');
|
|
76
|
+
let files;
|
|
77
|
+
try {
|
|
78
|
+
files = fs.readdirSync(reportDir);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null; // no reports dir → first run, nothing to compare
|
|
82
|
+
}
|
|
83
|
+
const prior = files
|
|
84
|
+
.filter((f) => f.startsWith(PREFIX) && f.endsWith(SUFFIX))
|
|
85
|
+
.map((f) => ({ file: f, date: f.slice(PREFIX.length, f.length - SUFFIX.length) }))
|
|
86
|
+
.filter((e) => DATE_RE.test(e.date) && e.date < currentDate)
|
|
87
|
+
.sort((a, b) => (a.date < b.date ? 1 : -1)); // most recent first
|
|
88
|
+
const currentSet = [...new Set(currentTools)];
|
|
89
|
+
for (const entry of prior) {
|
|
90
|
+
const prevTools = readToolsUsed(path.join(reportDir, entry.file));
|
|
91
|
+
if (!prevTools)
|
|
92
|
+
continue; // unreadable → try the next-most-recent
|
|
93
|
+
const prevSet = new Set(prevTools);
|
|
94
|
+
const added = currentSet.filter((t) => !prevSet.has(t)).sort();
|
|
95
|
+
// First readable prior report is the comparison point, added-or-not.
|
|
96
|
+
return added.length > 0 ? { added, previousDate: entry.date } : null;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/** Parse `toolsUsed` from a persisted detailed report. Fail-open → null. */
|
|
101
|
+
function readToolsUsed(filePath) {
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
104
|
+
if (Array.isArray(parsed.toolsUsed)) {
|
|
105
|
+
return parsed.toolsUsed.filter((t) => typeof t === 'string');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// unreadable / malformed → skip
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=scanner-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-drift.js","sourceRoot":"","sources":["../../../src/analyzers/security/scanner-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shallow.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/shallow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"shallow.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/shallow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,CAqF1E;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CA2CxE"}
|
|
@@ -19,6 +19,7 @@ exports.scoreSecurityDimension = scoreSecurityDimension;
|
|
|
19
19
|
* same number from the same partitioned inputs.
|
|
20
20
|
*/
|
|
21
21
|
const scoring_1 = require("../../scoring");
|
|
22
|
+
const annotate_1 = require("../../allowlist/annotate");
|
|
22
23
|
/**
|
|
23
24
|
* Build the canonical `SecurityScoreInput` from the health-side
|
|
24
25
|
* `ScoreInput`. Each field maps to its data source:
|
|
@@ -54,9 +55,13 @@ function toSecurityScoreInput(input) {
|
|
|
54
55
|
// `m.tlsDisabledCount` and `m.evalCount` as separate signals; the
|
|
55
56
|
// aggregate now carries the tls-bypass findings directly (with
|
|
56
57
|
// file/line and post-dedup), so those manual adds are gone.
|
|
58
|
+
// The score reads the allowlist-adjusted `scoreableCodeBySeverity`
|
|
59
|
+
// (excludes findings reviewed-and-accepted as false-positive /
|
|
60
|
+
// test-fixture); reports keep reading the raw `codeBySeverity`. Same
|
|
61
|
+
// source either way — the aggregator computes both.
|
|
57
62
|
let codeFindings;
|
|
58
63
|
if (c.securityAggregate) {
|
|
59
|
-
codeFindings = { ...c.securityAggregate.
|
|
64
|
+
codeFindings = { ...c.securityAggregate.scoreableCodeBySeverity };
|
|
60
65
|
}
|
|
61
66
|
else {
|
|
62
67
|
// Legacy fallback (test fixtures, pre-2.4.7 callers without
|
|
@@ -86,7 +91,17 @@ function toSecurityScoreInput(input) {
|
|
|
86
91
|
low: c.depVulns?.counts.low ?? 0,
|
|
87
92
|
};
|
|
88
93
|
return {
|
|
89
|
-
|
|
94
|
+
// Secret count for scoring EXCLUDES findings reviewed-and-accepted
|
|
95
|
+
// as false-positive / test-fixture, so a repo that has triaged its
|
|
96
|
+
// flagged secrets isn't held at the committed-credentials cap on
|
|
97
|
+
// noise it has already accepted. Read
|
|
98
|
+
// from the annotated aggregate when present (its secret category
|
|
99
|
+
// carries the allowlist status); fall back to the raw capability
|
|
100
|
+
// count for legacy ScoreInputs with no aggregate. accepted-risk /
|
|
101
|
+
// deferred are NOT lifted — those accept a real exposure.
|
|
102
|
+
secretFindings: c.securityAggregate
|
|
103
|
+
? c.securityAggregate.findingsByCategory.secret.filter((f) => !(f.allowlisted && (0, annotate_1.allowlistLiftsScore)(f.allowlistCategory))).length
|
|
104
|
+
: (c.secrets?.findings.length ?? 0),
|
|
90
105
|
privateKeyFiles: m.privateKeyFiles,
|
|
91
106
|
envFilesInGit: m.envFilesInGit,
|
|
92
107
|
codeFindings,
|
|
@@ -98,6 +113,13 @@ function toSecurityScoreInput(input) {
|
|
|
98
113
|
// is a downgrade; an explicit `false` from the gather is the only
|
|
99
114
|
// signal we trust to apply it.
|
|
100
115
|
depVulnsAvailable: c.depVulnsAvailability?.available ?? true,
|
|
116
|
+
// Same default-true contract as depVulnsAvailable — the
|
|
117
|
+
// explicit `false` from the gather is the only signal we trust to
|
|
118
|
+
// apply an uncertainty cap. Closes the asymmetry where a missing
|
|
119
|
+
// dep scan capped the score but missing secret/code scanners
|
|
120
|
+
// silently scored as clean.
|
|
121
|
+
secretsAvailable: c.secretsAvailability?.available ?? true,
|
|
122
|
+
codePatternsAvailable: c.codePatternsAvailability?.available ?? true,
|
|
101
123
|
};
|
|
102
124
|
}
|
|
103
125
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shallow.js","sourceRoot":"","sources":["../../../src/analyzers/security/shallow.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"shallow.js","sourceRoot":"","sources":["../../../src/analyzers/security/shallow.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Security analyzer types.
|
|
3
3
|
*/
|
|
4
4
|
import type { DepVulnFinding } from '../../languages/capabilities/types';
|
|
5
|
+
import type { AllowlistCategory } from '../../allowlist/categories';
|
|
6
|
+
import type { ScannerCoverageDrift } from './scanner-drift';
|
|
5
7
|
export type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
6
8
|
export type FindingCategory = 'secret' | 'code' | 'config' | 'dependency';
|
|
7
9
|
export interface SecurityFinding {
|
|
@@ -13,6 +15,45 @@ export interface SecurityFinding {
|
|
|
13
15
|
file: string;
|
|
14
16
|
line: number;
|
|
15
17
|
tool: string;
|
|
18
|
+
/**
|
|
19
|
+
* True when an ACTIVE (unexpired) allowlist entry matches this
|
|
20
|
+
* finding's fingerprint. The finding is still reported in full (raw
|
|
21
|
+
* counts are unchanged — dxkit's raw-truth model holds), but renderers
|
|
22
|
+
* surface "(N allowlisted)" alongside the subtotal so a reviewer isn't
|
|
23
|
+
* alarmed by, e.g., 8 CRITICAL secrets when 7 are accepted test
|
|
24
|
+
* fixtures. Absent (undefined) when no allowlist exists or no entry
|
|
25
|
+
* matches. Only code/secret/config findings (which carry a
|
|
26
|
+
* fingerprint) are annotated; dependency findings are keyed by tuple
|
|
27
|
+
* through a producer and are out of scope here.
|
|
28
|
+
*/
|
|
29
|
+
allowlisted?: boolean;
|
|
30
|
+
/** The matched allowlist entry's category (`test-fixture`,
|
|
31
|
+
* `false-positive`, `accepted-risk`, ...) so renderers can explain
|
|
32
|
+
* WHY the finding is suppressed, not just that it is. Present only
|
|
33
|
+
* when `allowlisted` is true. */
|
|
34
|
+
allowlistCategory?: AllowlistCategory;
|
|
35
|
+
/**
|
|
36
|
+
* Content-anchored identity material. Carried from the gather
|
|
37
|
+
* boundary through the aggregator so the finding's durable identity
|
|
38
|
+
* can be anchored to WHAT it is, not WHERE it sits (the line). These
|
|
39
|
+
* are additive plumbing: until the content-anchored migration the
|
|
40
|
+
* identity layer still hashes the line; afterward it hashes the
|
|
41
|
+
* content anchor and `line` becomes display metadata only.
|
|
42
|
+
*
|
|
43
|
+
* - `contentAnchor` — the fully-resolved anchor when known at gather:
|
|
44
|
+
* for secrets it's the salted HMAC of the value; for config it's
|
|
45
|
+
* `''`. For CODE findings it's NOT set at gather (it needs `scope`
|
|
46
|
+
* + ordinal); the aggregator stamps it.
|
|
47
|
+
* - `spanHash` — the 16-char hash of a code finding's normalized
|
|
48
|
+
* matched span (semgrep gather). Combined with `scope` + ordinal by
|
|
49
|
+
* the aggregator to build the code `contentAnchor`.
|
|
50
|
+
* - `scope` — the enclosing symbol (function/class) for a code
|
|
51
|
+
* finding, attached by the graph scope pre-pass; `''`/absent →
|
|
52
|
+
* file-level fallback.
|
|
53
|
+
*/
|
|
54
|
+
contentAnchor?: string;
|
|
55
|
+
spanHash?: string;
|
|
56
|
+
scope?: string;
|
|
16
57
|
}
|
|
17
58
|
export interface DepVulnSummary {
|
|
18
59
|
critical: number;
|
|
@@ -22,8 +63,8 @@ export interface DepVulnSummary {
|
|
|
22
63
|
total: number;
|
|
23
64
|
tool: string | null;
|
|
24
65
|
/** Per-advisory detail concatenated across every active pack. Empty
|
|
25
|
-
*
|
|
26
|
-
*
|
|
66
|
+
* when no provider returned findings (counts may still be non-zero
|
|
67
|
+
* for legacy pack output that only emits aggregate counts). */
|
|
27
68
|
findings: DepVulnFinding[];
|
|
28
69
|
/**
|
|
29
70
|
* D025b (2.4.7): true if at least one active pack's depVulns gather
|
|
@@ -53,8 +94,8 @@ export interface SecurityReport {
|
|
|
53
94
|
branch: string;
|
|
54
95
|
summary: {
|
|
55
96
|
/** Combined code+secret+config severity counts. Preserved for
|
|
56
|
-
*
|
|
57
|
-
*
|
|
97
|
+
* backward-compat with detailed report + dashboard consumers
|
|
98
|
+
* that already read this shape. */
|
|
58
99
|
findings: {
|
|
59
100
|
critical: number;
|
|
60
101
|
high: number;
|
|
@@ -91,9 +132,28 @@ export interface SecurityReport {
|
|
|
91
132
|
total: number;
|
|
92
133
|
};
|
|
93
134
|
dependencies: DepVulnSummary;
|
|
135
|
+
/**
|
|
136
|
+
* Whether the secret / code-pattern scans actually ran,
|
|
137
|
+
* from `aggregate.provenance.{secrets,codePatterns}.ran`. The
|
|
138
|
+
* standalone-side score adapter (`countsFromReport`) plumbs these
|
|
139
|
+
* into the uncertainty caps, mirroring `dependencies.available` —
|
|
140
|
+
* a "0 secrets" subtotal printed next to "Sources: (none)" must
|
|
141
|
+
* not score as a confident clean. Optional: report JSONs saved
|
|
142
|
+
* before 2.10 lack them (adapter defaults to true).
|
|
143
|
+
*/
|
|
144
|
+
secretsAvailable?: boolean;
|
|
145
|
+
codePatternsAvailable?: boolean;
|
|
94
146
|
};
|
|
95
147
|
findings: SecurityFinding[];
|
|
96
148
|
toolsUsed: string[];
|
|
97
149
|
toolsUnavailable: string[];
|
|
150
|
+
/**
|
|
151
|
+
* Scanners added since the most recent prior vuln-scan report,
|
|
152
|
+
* when any. Present only when the active scanner set grew run-over-run
|
|
153
|
+
* — drives the "coverage expanded; findings newly visible, not newly
|
|
154
|
+
* introduced" note that explains an otherwise-baffling score change on
|
|
155
|
+
* an unchanged commit. Absent on a first run or a stable scanner set.
|
|
156
|
+
*/
|
|
157
|
+
scannerDrift?: ScannerCoverageDrift;
|
|
98
158
|
}
|
|
99
159
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;qCAGiC;IACjC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;mEAE+D;IAC/D,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP;;2CAEmC;QACnC,QAAQ,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACzF;;;;;;;WAOG;QACH,QAAQ,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACzF;;;;;WAKG;QACH,WAAW,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5F,YAAY,EAAE,cAAc,CAAC;QAC7B;;;;;;;;WAQG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;KACjC,CAAC;IACF,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,oBAAoB,CAAC;CACrC"}
|
|
@@ -6,19 +6,27 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Two fingerprint families live here:
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* 1. Dependency-advisory fingerprints — stable hash of
|
|
10
|
+
* `(package, canonicalAdvisoryId)`. Used by `gatherDepVulns` +
|
|
11
|
+
* BoM. Excludes severity / cvssScore / enrichment fields
|
|
12
|
+
* (epssScore, kev, reachable, riskScore), producer `tool`, and
|
|
13
|
+
* `upgradeAdvice` / `upgradePlan` so re-scoring the same advisory
|
|
14
|
+
* against the same install never mints a new identity. Crucially
|
|
15
|
+
* it also excludes `installedVersion`: that value is only known
|
|
16
|
+
* when the dependency tree is installed (npm-audit reads
|
|
17
|
+
* node_modules), so a lockfile-only scanner (osv-scanner, or any
|
|
18
|
+
* gather in a bare git worktree) omits it — and including it forked
|
|
19
|
+
* the SAME advisory into two identities depending on the scan
|
|
20
|
+
* environment. The version is display metadata, not identity:
|
|
21
|
+
* bumping to a still-vulnerable version is the same finding, and
|
|
22
|
+
* bumping to a fixed version makes the finding disappear on its own.
|
|
15
23
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* 2. Code/secret/config-finding fingerprints — stable hash of
|
|
25
|
+
* `(canonicalRule, file, lineWindow)`. The canonical-rule map
|
|
26
|
+
* collapses cross-tool overlaps (e.g. semgrep + a per-language
|
|
27
|
+
* grep-based pattern both reporting the same TLS-bypass
|
|
28
|
+
* construct). The line-window absorbs the small offset between
|
|
29
|
+
* tools that report the declaration vs. the assignment.
|
|
22
30
|
*
|
|
23
31
|
* Both families share format: 16-char lowercase hex (first 8 bytes of
|
|
24
32
|
* SHA-1). Short enough to embed inline in reports, long enough to make
|
|
@@ -27,16 +35,49 @@
|
|
|
27
35
|
*/
|
|
28
36
|
import type { DepVulnFinding } from '../../languages/capabilities/types';
|
|
29
37
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
38
|
+
* Canonical advisory id for dep-vuln identity. Scanners label the same
|
|
39
|
+
* advisory differently — npm-audit emits an uppercase `GHSA-…`, while
|
|
40
|
+
* osv-scanner may primary an `OSV-…` / `CVE-…` / `GHSA-…` id and carry
|
|
41
|
+
* the rest in `aliases`. Collapse them to one token so the SAME
|
|
42
|
+
* vulnerability fingerprints identically regardless of which tool found
|
|
43
|
+
* it: prefer GHSA (the namespace every supported scanner shares), then
|
|
44
|
+
* CVE (the next-best cross-tool token), else the producer's own id.
|
|
45
|
+
* Lowercased so `GHSA-AB` and `ghsa-ab` don't fork identity.
|
|
46
|
+
*/
|
|
47
|
+
export declare function canonicalAdvisoryId(finding: {
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly aliases?: readonly string[];
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Stable 16-char hex fingerprint for one DepVulnFinding. Input tuple is
|
|
53
|
+
* NUL-separated (not present in any legal package name / advisory id) so
|
|
54
|
+
* distinct tuples can never collide via concatenation tricks.
|
|
33
55
|
*
|
|
34
|
-
*
|
|
35
|
-
* version
|
|
36
|
-
*
|
|
37
|
-
*
|
|
56
|
+
* Identity is `(package, canonicalAdvisoryId)` — deliberately NOT the
|
|
57
|
+
* installed version (see the module header): the version is unavailable
|
|
58
|
+
* to lockfile-only scanners, so including it forked identity by scan
|
|
59
|
+
* environment.
|
|
38
60
|
*/
|
|
39
|
-
export declare function computeFingerprint(finding:
|
|
61
|
+
export declare function computeFingerprint(finding: {
|
|
62
|
+
readonly package: string;
|
|
63
|
+
readonly id: string;
|
|
64
|
+
readonly aliases?: readonly string[];
|
|
65
|
+
}): string;
|
|
66
|
+
/**
|
|
67
|
+
* Pre-2.11 dependency-advisory fingerprint: `(package, installedVersion,
|
|
68
|
+
* id)`. Superseded by `computeFingerprint` (which drops the
|
|
69
|
+
* environment-dependent installed version and canonicalizes the advisory
|
|
70
|
+
* id), but retained verbatim so the identity-scheme migrator can
|
|
71
|
+
* recompute a finding's PRIOR-scheme id and remap allowlist entries onto
|
|
72
|
+
* the current scheme. Never delete a shipped scheme's id function — a
|
|
73
|
+
* migration from it must always be able to reproduce its output
|
|
74
|
+
* byte-for-byte. Not used to mint new identities.
|
|
75
|
+
*/
|
|
76
|
+
export declare function computeFingerprintV1(finding: {
|
|
77
|
+
readonly package: string;
|
|
78
|
+
readonly installedVersion?: string;
|
|
79
|
+
readonly id: string;
|
|
80
|
+
}): string;
|
|
40
81
|
/**
|
|
41
82
|
* Stamp `fingerprint` on every finding in place. Called once in
|
|
42
83
|
* `gatherDepVulns` after cross-pack merge + enrichment so every
|
|
@@ -99,6 +140,78 @@ export declare function lineWindowFor(line: number): number;
|
|
|
99
140
|
* code-finding fingerprints share a downstream type contract.
|
|
100
141
|
*/
|
|
101
142
|
export declare function computeCodeFingerprint(canonicalRule: string, file: string, line: number): string;
|
|
143
|
+
/**
|
|
144
|
+
* Normalize a matched code span so cosmetic reformatting (reindentation,
|
|
145
|
+
* collapsed vs expanded whitespace, trailing space) doesn't re-mint
|
|
146
|
+
* identity. Runs of whitespace collapse to a single space; ends trimmed.
|
|
147
|
+
* Deliberately conservative — it does NOT strip comments or rename
|
|
148
|
+
* identifiers, so a real change to the construct still re-mints.
|
|
149
|
+
*/
|
|
150
|
+
export declare function normalizeSpan(span: string): string;
|
|
151
|
+
/** 16-char hex hash of a normalized matched span. */
|
|
152
|
+
export declare function spanHash(span: string): string;
|
|
153
|
+
/**
|
|
154
|
+
* Build the content anchor for a CODE finding: `scope\0spanHash\0ordinal`.
|
|
155
|
+
* `scope` is the enclosing symbol (graph-resolved) or '' (file-level
|
|
156
|
+
* fallback). `ordinal` is the index among findings sharing the same
|
|
157
|
+
* `(scope, spanHash)` in document order, so identical constructs in one
|
|
158
|
+
* scope stay distinct. NUL-separated so the parts can't collide via
|
|
159
|
+
* concatenation.
|
|
160
|
+
*/
|
|
161
|
+
export declare function codeContentAnchor(scope: string, span: string, ordinal: number): string;
|
|
162
|
+
/**
|
|
163
|
+
* Build a code content anchor from an ALREADY-HASHED span. The gather
|
|
164
|
+
* boundary hashes the matched span once (`spanHash`) and carries only
|
|
165
|
+
* that 16-char digest downstream — never the raw source text — so the
|
|
166
|
+
* matched code never bloats reports or rides through the dashboard /
|
|
167
|
+
* JSON surfaces. The aggregator, which knows the enclosing `scope` (from
|
|
168
|
+
* the graph scope pre-pass) and the in-scope `ordinal`, assembles the
|
|
169
|
+
* final anchor from that carried digest via this helper. Equivalent to
|
|
170
|
+
* `codeContentAnchor` when fed `spanHash(span)`.
|
|
171
|
+
*/
|
|
172
|
+
export declare function codeContentAnchorFromHash(scope: string, spanHashHex: string, ordinal: number): string;
|
|
173
|
+
/**
|
|
174
|
+
* Build the content anchor for a SECRET finding: `secret\0<ordinal>`.
|
|
175
|
+
* The `(canonicalRule, file)` half of identity already lives in
|
|
176
|
+
* `computeContentFingerprint`, so the anchor only has to disambiguate
|
|
177
|
+
* multiple secrets of the same rule in the same file — the ordinal does
|
|
178
|
+
* that, assigned in document order by the aggregator.
|
|
179
|
+
*
|
|
180
|
+
* Crucially it carries NEITHER the captured value NOR the salt. That
|
|
181
|
+
* makes a secret's per-occurrence identity byte-identical across scanners
|
|
182
|
+
* (gitleaks' `Secret` field and the grep fallback's capture group differ)
|
|
183
|
+
* and across environments (the salt resolves differently via env var /
|
|
184
|
+
* file / root-SHA), which is what a baseline/allowlist needs to stay
|
|
185
|
+
* matched between a developer's machine and CI. The `secret` prefix
|
|
186
|
+
* namespaces it away from code anchors (`scope\0spanHash\0ordinal`) so the
|
|
187
|
+
* two schemes can never collide.
|
|
188
|
+
*
|
|
189
|
+
* The value HMAC is not lost — the separate `secret-hmac` identity kind
|
|
190
|
+
* still pins it, for recognizing the same value relocating across files.
|
|
191
|
+
*/
|
|
192
|
+
export declare function secretContentAnchor(ordinal: number): string;
|
|
193
|
+
/**
|
|
194
|
+
* The tool-independent rule discriminator for SECRET identity. Unlike code
|
|
195
|
+
* findings — where two different rules firing on one construct are two
|
|
196
|
+
* distinct findings, so the rule must stay in identity — every secret
|
|
197
|
+
* detection means the same thing ("a hardcoded/leaked credential", CWE-798).
|
|
198
|
+
* Folding them onto one constant makes a secret's identity independent of
|
|
199
|
+
* WHICH scanner found it and under what rule name (gitleaks `aws-access-key`
|
|
200
|
+
* vs the grep fallback's `hardcoded-password` describe the same leak). Used
|
|
201
|
+
* in place of `canonicalRuleFor(tool, rule)` when fingerprinting secrets;
|
|
202
|
+
* the per-tool canonical rule is still used for intra-run dedup grouping and
|
|
203
|
+
* survives on the finding as display metadata.
|
|
204
|
+
*/
|
|
205
|
+
export declare const SECRET_CANONICAL_RULE = "canonical:secret";
|
|
206
|
+
/**
|
|
207
|
+
* Content-anchored finding fingerprint (scheme v2). Identity is
|
|
208
|
+
* `(canonicalRule, file, contentAnchor)` — the anchor carries the
|
|
209
|
+
* stable, location-independent content (built by the caller per kind:
|
|
210
|
+
* secret=HMAC, code=`codeContentAnchor(...)`, config=''). A finding that
|
|
211
|
+
* moves to a new line keeps its fingerprint; it re-mints only when the
|
|
212
|
+
* matched content (or, for code, its enclosing symbol) changes.
|
|
213
|
+
*/
|
|
214
|
+
export declare function computeContentFingerprint(canonicalRule: string, file: string, contentAnchor: string): string;
|
|
102
215
|
/**
|
|
103
216
|
* HMAC-SHA256 of a detected secret value, keyed by a per-repo salt.
|
|
104
217
|
* The output is 16-char lowercase hex (first 8 bytes of the 32-byte
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/fingerprint.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,GAAG,MAAM,CAST;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,GAAG,MAAM,CAGT;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAGT;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAIlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,MAAM,EAAE,CAMrF;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAmBzD,CAAC;AAEH,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhG;AAwCD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,qDAAqD;AACrD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,GACpB,MAAM,CAGR;AAID;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtE"}
|