@vyuhlabs/dxkit 2.4.6 → 2.4.7
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 +885 -0
- package/README.md +131 -26
- package/dist/analysis-result.d.ts +112 -0
- package/dist/analysis-result.d.ts.map +1 -0
- package/dist/analysis-result.js +52 -0
- package/dist/analysis-result.js.map +1 -0
- package/dist/analyzers/bom/detailed.d.ts.map +1 -1
- package/dist/analyzers/bom/detailed.js +19 -0
- package/dist/analyzers/bom/detailed.js.map +1 -1
- package/dist/analyzers/bom/gather.d.ts +27 -26
- package/dist/analyzers/bom/gather.d.ts.map +1 -1
- package/dist/analyzers/bom/gather.js +26 -87
- package/dist/analyzers/bom/gather.js.map +1 -1
- package/dist/analyzers/bom/index.d.ts +0 -7
- package/dist/analyzers/bom/index.d.ts.map +1 -1
- package/dist/analyzers/bom/index.js +98 -48
- package/dist/analyzers/bom/index.js.map +1 -1
- package/dist/analyzers/bom/types.d.ts +11 -13
- package/dist/analyzers/bom/types.d.ts.map +1 -1
- package/dist/analyzers/cache.d.ts +95 -0
- package/dist/analyzers/cache.d.ts.map +1 -0
- package/dist/analyzers/cache.js +309 -0
- package/dist/analyzers/cache.js.map +1 -0
- package/dist/analyzers/coverage-runner.d.ts +56 -0
- package/dist/analyzers/coverage-runner.d.ts.map +1 -0
- package/dist/analyzers/coverage-runner.js +72 -0
- package/dist/analyzers/coverage-runner.js.map +1 -0
- package/dist/analyzers/dashboard/index.d.ts +24 -0
- package/dist/analyzers/dashboard/index.d.ts.map +1 -0
- package/dist/analyzers/dashboard/index.js +666 -0
- package/dist/analyzers/dashboard/index.js.map +1 -0
- package/dist/analyzers/developer/gather.d.ts.map +1 -1
- package/dist/analyzers/developer/gather.js +205 -37
- package/dist/analyzers/developer/gather.js.map +1 -1
- package/dist/analyzers/developer/index.d.ts +1 -1
- package/dist/analyzers/developer/index.d.ts.map +1 -1
- package/dist/analyzers/developer/index.js +19 -8
- package/dist/analyzers/developer/index.js.map +1 -1
- package/dist/analyzers/dispatcher.d.ts +37 -0
- package/dist/analyzers/dispatcher.d.ts.map +1 -1
- package/dist/analyzers/dispatcher.js +56 -9
- package/dist/analyzers/dispatcher.js.map +1 -1
- package/dist/analyzers/docs/shallow.d.ts +17 -5
- package/dist/analyzers/docs/shallow.d.ts.map +1 -1
- package/dist/analyzers/docs/shallow.js +65 -2
- package/dist/analyzers/docs/shallow.js.map +1 -1
- package/dist/analyzers/dx/shallow.d.ts +17 -5
- package/dist/analyzers/dx/shallow.d.ts.map +1 -1
- package/dist/analyzers/dx/shallow.js +66 -2
- package/dist/analyzers/dx/shallow.js.map +1 -1
- package/dist/analyzers/health/actions.d.ts +1 -1
- package/dist/analyzers/health/actions.d.ts.map +1 -1
- package/dist/analyzers/health/actions.js +27 -9
- package/dist/analyzers/health/actions.js.map +1 -1
- package/dist/analyzers/health/detailed.d.ts +2 -1
- package/dist/analyzers/health/detailed.d.ts.map +1 -1
- package/dist/analyzers/health/detailed.js +11 -7
- package/dist/analyzers/health/detailed.js.map +1 -1
- package/dist/analyzers/health.d.ts +27 -0
- package/dist/analyzers/health.d.ts.map +1 -1
- package/dist/analyzers/health.js +271 -33
- package/dist/analyzers/health.js.map +1 -1
- package/dist/analyzers/licenses/gather.d.ts +35 -8
- package/dist/analyzers/licenses/gather.d.ts.map +1 -1
- package/dist/analyzers/licenses/gather.js +70 -13
- package/dist/analyzers/licenses/gather.js.map +1 -1
- package/dist/analyzers/licenses/index.d.ts +1 -1
- package/dist/analyzers/licenses/index.d.ts.map +1 -1
- package/dist/analyzers/licenses/index.js +52 -11
- package/dist/analyzers/licenses/index.js.map +1 -1
- package/dist/analyzers/licenses/types.d.ts +15 -0
- package/dist/analyzers/licenses/types.d.ts.map +1 -1
- package/dist/analyzers/maintainability/shallow.d.ts +17 -5
- package/dist/analyzers/maintainability/shallow.d.ts.map +1 -1
- package/dist/analyzers/maintainability/shallow.js +80 -2
- package/dist/analyzers/maintainability/shallow.js.map +1 -1
- package/dist/analyzers/quality/detailed.d.ts.map +1 -1
- package/dist/analyzers/quality/detailed.js +4 -6
- package/dist/analyzers/quality/detailed.js.map +1 -1
- package/dist/analyzers/quality/gather.d.ts +1 -14
- package/dist/analyzers/quality/gather.d.ts.map +1 -1
- package/dist/analyzers/quality/gather.js +48 -137
- package/dist/analyzers/quality/gather.js.map +1 -1
- package/dist/analyzers/quality/index.d.ts +9 -2
- package/dist/analyzers/quality/index.d.ts.map +1 -1
- package/dist/analyzers/quality/index.js +189 -117
- package/dist/analyzers/quality/index.js.map +1 -1
- package/dist/analyzers/quality/shallow.d.ts +50 -5
- package/dist/analyzers/quality/shallow.d.ts.map +1 -1
- package/dist/analyzers/quality/shallow.js +155 -2
- package/dist/analyzers/quality/shallow.js.map +1 -1
- package/dist/analyzers/quality/types.d.ts +14 -0
- package/dist/analyzers/quality/types.d.ts.map +1 -1
- package/dist/analyzers/security/actions.d.ts +11 -4
- package/dist/analyzers/security/actions.d.ts.map +1 -1
- package/dist/analyzers/security/actions.js +87 -37
- package/dist/analyzers/security/actions.js.map +1 -1
- package/dist/analyzers/security/aggregator.d.ts +236 -0
- package/dist/analyzers/security/aggregator.d.ts.map +1 -0
- package/dist/analyzers/security/aggregator.js +347 -0
- package/dist/analyzers/security/aggregator.js.map +1 -0
- package/dist/analyzers/security/detailed.d.ts +2 -2
- package/dist/analyzers/security/detailed.d.ts.map +1 -1
- package/dist/analyzers/security/detailed.js +10 -9
- package/dist/analyzers/security/detailed.js.map +1 -1
- package/dist/analyzers/security/gather.d.ts +103 -1
- package/dist/analyzers/security/gather.d.ts.map +1 -1
- package/dist/analyzers/security/gather.js +281 -9
- package/dist/analyzers/security/gather.js.map +1 -1
- package/dist/analyzers/security/index.d.ts +15 -0
- package/dist/analyzers/security/index.d.ts.map +1 -1
- package/dist/analyzers/security/index.js +463 -50
- package/dist/analyzers/security/index.js.map +1 -1
- package/dist/analyzers/security/shallow.d.ts +50 -6
- package/dist/analyzers/security/shallow.d.ts.map +1 -1
- package/dist/analyzers/security/shallow.js +154 -2
- package/dist/analyzers/security/shallow.js.map +1 -1
- package/dist/analyzers/security/types.d.ts +51 -0
- package/dist/analyzers/security/types.d.ts.map +1 -1
- package/dist/analyzers/tests/detailed.d.ts.map +1 -1
- package/dist/analyzers/tests/detailed.js +2 -3
- package/dist/analyzers/tests/detailed.js.map +1 -1
- package/dist/analyzers/tests/gather.d.ts +2 -1
- package/dist/analyzers/tests/gather.d.ts.map +1 -1
- package/dist/analyzers/tests/gather.js +98 -69
- package/dist/analyzers/tests/gather.js.map +1 -1
- package/dist/analyzers/tests/index.d.ts +11 -2
- package/dist/analyzers/tests/index.d.ts.map +1 -1
- package/dist/analyzers/tests/index.js +83 -18
- package/dist/analyzers/tests/index.js.map +1 -1
- package/dist/analyzers/tests/shallow.d.ts +19 -5
- package/dist/analyzers/tests/shallow.d.ts.map +1 -1
- package/dist/analyzers/tests/shallow.js +89 -2
- package/dist/analyzers/tests/shallow.js.map +1 -1
- package/dist/analyzers/tests/types.d.ts +41 -1
- package/dist/analyzers/tests/types.d.ts.map +1 -1
- package/dist/analyzers/tools/autogen-header.d.ts +8 -0
- package/dist/analyzers/tools/autogen-header.d.ts.map +1 -0
- package/dist/analyzers/tools/autogen-header.js +107 -0
- package/dist/analyzers/tools/autogen-header.js.map +1 -0
- package/dist/analyzers/tools/cloc.d.ts.map +1 -1
- package/dist/analyzers/tools/cloc.js +36 -5
- package/dist/analyzers/tools/cloc.js.map +1 -1
- package/dist/analyzers/tools/debug-statements.d.ts +17 -0
- package/dist/analyzers/tools/debug-statements.d.ts.map +1 -0
- package/dist/analyzers/tools/debug-statements.js +58 -0
- package/dist/analyzers/tools/debug-statements.js.map +1 -0
- package/dist/analyzers/tools/default-exclusions.gitignore +28 -0
- package/dist/analyzers/tools/exclusions.d.ts +33 -6
- package/dist/analyzers/tools/exclusions.d.ts.map +1 -1
- package/dist/analyzers/tools/exclusions.js +95 -26
- package/dist/analyzers/tools/exclusions.js.map +1 -1
- package/dist/analyzers/tools/generic.d.ts +17 -2
- package/dist/analyzers/tools/generic.d.ts.map +1 -1
- package/dist/analyzers/tools/generic.js +206 -109
- package/dist/analyzers/tools/generic.js.map +1 -1
- package/dist/analyzers/tools/gitleaks.d.ts.map +1 -1
- package/dist/analyzers/tools/gitleaks.js +48 -1
- package/dist/analyzers/tools/gitleaks.js.map +1 -1
- package/dist/analyzers/tools/graphify.d.ts +30 -2
- package/dist/analyzers/tools/graphify.d.ts.map +1 -1
- package/dist/analyzers/tools/graphify.js +131 -15
- package/dist/analyzers/tools/graphify.js.map +1 -1
- package/dist/analyzers/tools/jscpd.d.ts +12 -2
- package/dist/analyzers/tools/jscpd.d.ts.map +1 -1
- package/dist/analyzers/tools/jscpd.js +129 -6
- package/dist/analyzers/tools/jscpd.js.map +1 -1
- package/dist/analyzers/tools/minified-detection.d.ts +9 -0
- package/dist/analyzers/tools/minified-detection.d.ts.map +1 -0
- package/dist/analyzers/tools/minified-detection.js +147 -0
- package/dist/analyzers/tools/minified-detection.js.map +1 -0
- package/dist/analyzers/tools/nuget-package-reference.d.ts +131 -0
- package/dist/analyzers/tools/nuget-package-reference.d.ts.map +1 -0
- package/dist/analyzers/tools/nuget-package-reference.js +175 -0
- package/dist/analyzers/tools/nuget-package-reference.js.map +1 -0
- package/dist/analyzers/tools/osv-scanner-deps.d.ts +3 -2
- package/dist/analyzers/tools/osv-scanner-deps.d.ts.map +1 -1
- package/dist/analyzers/tools/osv-scanner-deps.js +32 -14
- package/dist/analyzers/tools/osv-scanner-deps.js.map +1 -1
- package/dist/analyzers/tools/osv.d.ts +36 -0
- package/dist/analyzers/tools/osv.d.ts.map +1 -1
- package/dist/analyzers/tools/osv.js +26 -0
- package/dist/analyzers/tools/osv.js.map +1 -1
- package/dist/analyzers/tools/parallel.d.ts +1 -1
- package/dist/analyzers/tools/parallel.d.ts.map +1 -1
- package/dist/analyzers/tools/parallel.js +2 -2
- package/dist/analyzers/tools/parallel.js.map +1 -1
- package/dist/analyzers/tools/risk-score.d.ts +7 -0
- package/dist/analyzers/tools/risk-score.d.ts.map +1 -1
- package/dist/analyzers/tools/risk-score.js +9 -2
- package/dist/analyzers/tools/risk-score.js.map +1 -1
- package/dist/analyzers/tools/run-tests-helper.d.ts +43 -0
- package/dist/analyzers/tools/run-tests-helper.d.ts.map +1 -0
- package/dist/analyzers/tools/run-tests-helper.js +156 -0
- package/dist/analyzers/tools/run-tests-helper.js.map +1 -0
- package/dist/analyzers/tools/runner.d.ts.map +1 -1
- package/dist/analyzers/tools/runner.js +75 -12
- package/dist/analyzers/tools/runner.js.map +1 -1
- package/dist/analyzers/tools/semgrep.d.ts +39 -2
- package/dist/analyzers/tools/semgrep.d.ts.map +1 -1
- package/dist/analyzers/tools/semgrep.js +131 -9
- package/dist/analyzers/tools/semgrep.js.map +1 -1
- package/dist/analyzers/tools/timing.d.ts +17 -3
- package/dist/analyzers/tools/timing.d.ts.map +1 -1
- package/dist/analyzers/tools/timing.js +36 -14
- package/dist/analyzers/tools/timing.js.map +1 -1
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
- package/dist/analyzers/tools/tool-registry.js +11 -1
- package/dist/analyzers/tools/tool-registry.js.map +1 -1
- package/dist/analyzers/tools/tools-unavailable-prose.d.ts +18 -0
- package/dist/analyzers/tools/tools-unavailable-prose.d.ts.map +1 -0
- package/dist/analyzers/tools/tools-unavailable-prose.js +69 -0
- package/dist/analyzers/tools/tools-unavailable-prose.js.map +1 -0
- package/dist/analyzers/tools/upgrade-plan-resolver.d.ts.map +1 -1
- package/dist/analyzers/tools/upgrade-plan-resolver.js +7 -0
- package/dist/analyzers/tools/upgrade-plan-resolver.js.map +1 -1
- package/dist/analyzers/tools/vendored-advisor.d.ts +43 -0
- package/dist/analyzers/tools/vendored-advisor.d.ts.map +1 -0
- package/dist/analyzers/tools/vendored-advisor.js +107 -0
- package/dist/analyzers/tools/vendored-advisor.js.map +1 -0
- package/dist/analyzers/tools/walk-paths.d.ts +78 -0
- package/dist/analyzers/tools/walk-paths.d.ts.map +1 -0
- package/dist/analyzers/tools/walk-paths.js +150 -0
- package/dist/analyzers/tools/walk-paths.js.map +1 -0
- package/dist/analyzers/tools/walk-source-files.d.ts +70 -0
- package/dist/analyzers/tools/walk-source-files.d.ts.map +1 -0
- package/dist/analyzers/tools/walk-source-files.js +369 -0
- package/dist/analyzers/tools/walk-source-files.js.map +1 -0
- package/dist/analyzers/types.d.ts +204 -4
- package/dist/analyzers/types.d.ts.map +1 -1
- package/dist/analyzers/xlsx/bom.d.ts.map +1 -1
- package/dist/analyzers/xlsx/bom.js +8 -1
- package/dist/analyzers/xlsx/bom.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +557 -189
- package/dist/cli.js.map +1 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +24 -7
- package/dist/detect.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +103 -53
- package/dist/doctor.js.map +1 -1
- package/dist/languages/capabilities/provider.d.ts +130 -1
- package/dist/languages/capabilities/provider.d.ts.map +1 -1
- package/dist/languages/capabilities/types.d.ts +68 -7
- package/dist/languages/capabilities/types.d.ts.map +1 -1
- package/dist/languages/csharp.d.ts +15 -1
- package/dist/languages/csharp.d.ts.map +1 -1
- package/dist/languages/csharp.js +624 -146
- package/dist/languages/csharp.js.map +1 -1
- package/dist/languages/go.d.ts.map +1 -1
- package/dist/languages/go.js +89 -11
- package/dist/languages/go.js.map +1 -1
- package/dist/languages/index.d.ts +131 -2
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +206 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/java.d.ts.map +1 -1
- package/dist/languages/java.js +113 -26
- package/dist/languages/java.js.map +1 -1
- package/dist/languages/kotlin.d.ts.map +1 -1
- package/dist/languages/kotlin.js +132 -26
- package/dist/languages/kotlin.js.map +1 -1
- package/dist/languages/python.d.ts.map +1 -1
- package/dist/languages/python.js +149 -44
- package/dist/languages/python.js.map +1 -1
- package/dist/languages/ruby.d.ts +39 -1
- package/dist/languages/ruby.d.ts.map +1 -1
- package/dist/languages/ruby.js +178 -44
- package/dist/languages/ruby.js.map +1 -1
- package/dist/languages/rust.d.ts.map +1 -1
- package/dist/languages/rust.js +103 -16
- package/dist/languages/rust.js.map +1 -1
- package/dist/languages/types.d.ts +228 -5
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +201 -14
- package/dist/languages/typescript.js.map +1 -1
- package/dist/scoring/dimensions/documentation.d.ts +53 -0
- package/dist/scoring/dimensions/documentation.d.ts.map +1 -0
- package/dist/scoring/dimensions/documentation.js +106 -0
- package/dist/scoring/dimensions/documentation.js.map +1 -0
- package/dist/scoring/dimensions/dx.d.ts +53 -0
- package/dist/scoring/dimensions/dx.d.ts.map +1 -0
- package/dist/scoring/dimensions/dx.js +105 -0
- package/dist/scoring/dimensions/dx.js.map +1 -0
- package/dist/scoring/dimensions/maintainability.d.ts +53 -0
- package/dist/scoring/dimensions/maintainability.d.ts.map +1 -0
- package/dist/scoring/dimensions/maintainability.js +101 -0
- package/dist/scoring/dimensions/maintainability.js.map +1 -0
- package/dist/scoring/dimensions/quality.d.ts +108 -0
- package/dist/scoring/dimensions/quality.d.ts.map +1 -0
- package/dist/scoring/dimensions/quality.js +174 -0
- package/dist/scoring/dimensions/quality.js.map +1 -0
- package/dist/scoring/dimensions/security.d.ts +84 -0
- package/dist/scoring/dimensions/security.d.ts.map +1 -0
- package/dist/scoring/dimensions/security.js +135 -0
- package/dist/scoring/dimensions/security.js.map +1 -0
- package/dist/scoring/dimensions/testing.d.ts +56 -0
- package/dist/scoring/dimensions/testing.d.ts.map +1 -0
- package/dist/scoring/dimensions/testing.js +98 -0
- package/dist/scoring/dimensions/testing.js.map +1 -0
- package/dist/scoring/evaluator.d.ts +27 -0
- package/dist/scoring/evaluator.d.ts.map +1 -0
- package/dist/scoring/evaluator.js +124 -0
- package/dist/scoring/evaluator.js.map +1 -0
- package/dist/scoring/format.d.ts +34 -0
- package/dist/scoring/format.d.ts.map +1 -0
- package/dist/scoring/format.js +63 -0
- package/dist/scoring/format.js.map +1 -0
- package/dist/scoring/index.d.ts +37 -0
- package/dist/scoring/index.d.ts.map +1 -0
- package/dist/scoring/index.js +57 -0
- package/dist/scoring/index.js.map +1 -0
- package/dist/scoring/overall.d.ts +54 -0
- package/dist/scoring/overall.d.ts.map +1 -0
- package/dist/scoring/overall.js +76 -0
- package/dist/scoring/overall.js.map +1 -0
- package/dist/scoring/result.d.ts +111 -0
- package/dist/scoring/result.d.ts.map +1 -0
- package/dist/scoring/result.js +14 -0
- package/dist/scoring/result.js.map +1 -0
- package/dist/scoring/spec.d.ts +76 -0
- package/dist/scoring/spec.d.ts.map +1 -0
- package/dist/scoring/spec.js +22 -0
- package/dist/scoring/spec.js.map +1 -0
- package/dist/scoring/thresholds.d.ts +56 -0
- package/dist/scoring/thresholds.d.ts.map +1 -0
- package/dist/scoring/thresholds.js +75 -0
- package/dist/scoring/thresholds.js.map +1 -0
- package/dist/tools-cli.d.ts.map +1 -1
- package/dist/tools-cli.js +21 -2
- package/dist/tools-cli.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/.claude/commands/dashboard.md +17 -9
- package/dist/analyzers/scoring.d.ts +0 -49
- package/dist/analyzers/scoring.d.ts.map +0 -1
- package/dist/analyzers/scoring.js +0 -422
- package/dist/analyzers/scoring.js.map +0 -1
- package/dist/analyzers/security/scoring.d.ts +0 -29
- package/dist/analyzers/security/scoring.d.ts.map +0 -1
- package/dist/analyzers/security/scoring.js +0 -40
- package/dist/analyzers/security/scoring.js.map +0 -1
package/dist/languages/csharp.js
CHANGED
|
@@ -39,14 +39,20 @@ exports.parseDotnetVulnerableOutput = parseDotnetVulnerableOutput;
|
|
|
39
39
|
exports.parseProjectAssetsJson = parseProjectAssetsJson;
|
|
40
40
|
exports.buildCsharpTopLevelDepIndex = buildCsharpTopLevelDepIndex;
|
|
41
41
|
exports.findAllProjectAssetsJson = findAllProjectAssetsJson;
|
|
42
|
+
exports.findAllCsprojFiles = findAllCsprojFiles;
|
|
42
43
|
exports.mergeAssetParses = mergeAssetParses;
|
|
43
44
|
exports.extractCsharpImportsRaw = extractCsharpImportsRaw;
|
|
44
45
|
const fs = __importStar(require("fs"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
45
47
|
const path = __importStar(require("path"));
|
|
46
48
|
const exclusions_1 = require("../analyzers/tools/exclusions");
|
|
49
|
+
const nuget_package_reference_1 = require("../analyzers/tools/nuget-package-reference");
|
|
47
50
|
const osv_1 = require("../analyzers/tools/osv");
|
|
51
|
+
const osv_scanner_deps_1 = require("../analyzers/tools/osv-scanner-deps");
|
|
48
52
|
const runner_1 = require("../analyzers/tools/runner");
|
|
53
|
+
const run_tests_helper_1 = require("../analyzers/tools/run-tests-helper");
|
|
49
54
|
const tool_registry_1 = require("../analyzers/tools/tool-registry");
|
|
55
|
+
const walk_paths_1 = require("../analyzers/tools/walk-paths");
|
|
50
56
|
function dirHasMatching(dir, regex) {
|
|
51
57
|
try {
|
|
52
58
|
return fs.readdirSync(dir).some((name) => regex.test(name));
|
|
@@ -55,49 +61,32 @@ function dirHasMatching(dir, regex) {
|
|
|
55
61
|
return false;
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
|
-
function findMatchingRecursive(cwd, regex, maxDepth = 3) {
|
|
59
|
-
function search(dir, depth) {
|
|
60
|
-
if (depth > maxDepth)
|
|
61
|
-
return null;
|
|
62
|
-
let entries;
|
|
63
|
-
try {
|
|
64
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
for (const e of entries) {
|
|
70
|
-
if (e.name.startsWith('.') ||
|
|
71
|
-
['node_modules', 'bin', 'obj', 'TestResults', 'packages'].includes(e.name)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const full = path.join(dir, e.name);
|
|
75
|
-
if (e.isFile() && regex.test(e.name))
|
|
76
|
-
return full;
|
|
77
|
-
if (e.isDirectory()) {
|
|
78
|
-
const nested = search(full, depth + 1);
|
|
79
|
-
if (nested)
|
|
80
|
-
return nested;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
return search(cwd, 0);
|
|
86
|
-
}
|
|
87
64
|
function findCoberturaArtifact(cwd) {
|
|
88
65
|
// Common layouts:
|
|
89
66
|
// coverage/coverage.cobertura.xml (explicit run)
|
|
90
67
|
// TestResults/<guid>/coverage.cobertura.xml (default `dotnet test --collect`)
|
|
68
|
+
//
|
|
69
|
+
// The canonical walker rightly excludes `TestResults/` (a build-
|
|
70
|
+
// output subtree) from manifest/source discovery, but here we need
|
|
71
|
+
// to look INSIDE it. We run the walker scoped to the TestResults
|
|
72
|
+
// directory itself with `respectIgnore: false` so the bundled
|
|
73
|
+
// excludes don't fire — we're starting from inside the very subtree
|
|
74
|
+
// they were meant to prune. Depth-unlimited so future tooling that
|
|
75
|
+
// nests artifacts deeper than today's `TestResults/<guid>/` shape
|
|
76
|
+
// still finds them.
|
|
91
77
|
const top = path.join(cwd, 'coverage', 'coverage.cobertura.xml');
|
|
92
78
|
if (fs.existsSync(top))
|
|
93
79
|
return top;
|
|
94
80
|
const testResults = path.join(cwd, 'TestResults');
|
|
95
|
-
if (fs.existsSync(testResults))
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
if (!fs.existsSync(testResults))
|
|
82
|
+
return null;
|
|
83
|
+
const matches = (0, walk_paths_1.walkPaths)(testResults, {
|
|
84
|
+
extensions: ['.xml'],
|
|
85
|
+
respectIgnore: false,
|
|
86
|
+
}).filter((rel) => rel.endsWith('coverage.cobertura.xml'));
|
|
87
|
+
if (matches.length === 0)
|
|
88
|
+
return null;
|
|
89
|
+
return path.join(testResults, matches[0]);
|
|
101
90
|
}
|
|
102
91
|
function round1(n) {
|
|
103
92
|
return Math.round(n * 10) / 10;
|
|
@@ -218,6 +207,7 @@ function parseDotnetVulnerableOutput(raw) {
|
|
|
218
207
|
package: pkgId,
|
|
219
208
|
installedVersion: resolvedVersion,
|
|
220
209
|
tool: 'dotnet-vulnerable',
|
|
210
|
+
packId: 'csharp',
|
|
221
211
|
severity,
|
|
222
212
|
};
|
|
223
213
|
if (topLevelDep && topLevelDep.length > 0)
|
|
@@ -343,43 +333,39 @@ function buildCsharpTopLevelDepIndex(parsed) {
|
|
|
343
333
|
*
|
|
344
334
|
* Exported for test coverage.
|
|
345
335
|
*/
|
|
346
|
-
function findAllProjectAssetsJson(cwd
|
|
336
|
+
function findAllProjectAssetsJson(cwd) {
|
|
337
|
+
// Project-anchored discovery: every `obj/project.assets.json` sits
|
|
338
|
+
// next to a `.csproj`. Find every `.csproj` via the canonical walker
|
|
339
|
+
// (depth-unlimited, exclusion-aware), then probe each project's
|
|
340
|
+
// `obj/project.assets.json` directly. This replaces a parallel
|
|
341
|
+
// depth-capped walker that missed deep monorepos (dpl-studio's
|
|
342
|
+
// .csproj files sit 6–9 levels under repo root) and naturally
|
|
343
|
+
// inherits the same `.gitignore` / `.dxkit-ignore` / standard
|
|
344
|
+
// exclusion rules every other dxkit walker honors.
|
|
347
345
|
const out = [];
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
// Direct-child shortcut: if this directory is `obj`, collect the
|
|
359
|
-
// file right here before recursing siblings. Skipping recursion
|
|
360
|
-
// into `obj/` keeps the walk bounded (obj/ contains no deeper
|
|
361
|
-
// projects of its own).
|
|
362
|
-
if (path.basename(dir) === 'obj') {
|
|
363
|
-
for (const e of entries) {
|
|
364
|
-
if (e.isFile() && e.name === 'project.assets.json') {
|
|
365
|
-
out.push(path.join(dir, e.name));
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
for (const e of entries) {
|
|
371
|
-
if (e.name.startsWith('.') ||
|
|
372
|
-
['node_modules', 'bin', 'TestResults', 'packages'].includes(e.name)) {
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
if (e.isDirectory()) {
|
|
376
|
-
search(path.join(dir, e.name), depth + 1);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
346
|
+
for (const csproj of findAllCsprojFiles(cwd)) {
|
|
347
|
+
const assets = path.join(path.dirname(csproj), 'obj', 'project.assets.json');
|
|
348
|
+
if (fs.existsSync(assets))
|
|
349
|
+
out.push(assets);
|
|
379
350
|
}
|
|
380
|
-
search(cwd, 0);
|
|
381
351
|
return out;
|
|
382
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Locate every `.csproj` reachable from cwd within `maxDepth` levels.
|
|
355
|
+
* D025f (2.4.7): the direct-parsing fallback iterates this list when
|
|
356
|
+
* `dotnet list package` can't produce output (D036: cwd is a parent
|
|
357
|
+
* of the actual project files, dotnet has no way to pick one).
|
|
358
|
+
*
|
|
359
|
+
* Skips standard non-source dirs (node_modules / bin / obj /
|
|
360
|
+
* TestResults / packages). Unlike `findAllProjectAssetsJson` (which
|
|
361
|
+
* needs `obj/`), this walk's targets sit in project root directories
|
|
362
|
+
* so the obj-skip is fine.
|
|
363
|
+
*
|
|
364
|
+
* Exported for test coverage.
|
|
365
|
+
*/
|
|
366
|
+
function findAllCsprojFiles(cwd) {
|
|
367
|
+
return (0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj'] }).map((rel) => path.join(cwd, rel));
|
|
368
|
+
}
|
|
383
369
|
/**
|
|
384
370
|
* Merge multiple `project.assets.json` parse results into a single
|
|
385
371
|
* graph. `topLevels` unions across projects; `edges` unions the
|
|
@@ -434,35 +420,175 @@ function loadCsharpTopLevelDepIndex(cwd) {
|
|
|
434
420
|
return buildCsharpTopLevelDepIndex(mergeAssetParses(parses));
|
|
435
421
|
}
|
|
436
422
|
/**
|
|
437
|
-
*
|
|
438
|
-
* Consumed by `csharpDepVulnsProvider` (capability dispatcher). Runs
|
|
439
|
-
* independently of `dotnet-format` availability — historical bug where
|
|
440
|
-
* projects with `dotnet` but no `dotnet-format` saw zero vuln data.
|
|
423
|
+
* D025f (2.4.7) — direct PackageReference scan via osv-scanner.
|
|
441
424
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
425
|
+
* Fallback gather path that bypasses `dotnet list package` entirely.
|
|
426
|
+
* Used when (a) dotnet isn't installed or (b) dotnet ran but produced
|
|
427
|
+
* no output (D036: cwd is a parent directory and dotnet can't pick
|
|
428
|
+
* a project file).
|
|
429
|
+
*
|
|
430
|
+
* Architecture:
|
|
431
|
+
* 1. Walk every `.csproj` under cwd (depth 5).
|
|
432
|
+
* 2. Parse each via `parseCsprojPackageReferences`.
|
|
433
|
+
* 3. Union direct PackageReferences across all .csprojs (dedup by
|
|
434
|
+
* name@version; cross-csproj version collisions documented in
|
|
435
|
+
* the parser-helper layer as last-write-wins at lockfile time).
|
|
436
|
+
* 4. Generate ad-hoc `packages.lock.json` schema → temp file.
|
|
437
|
+
* 5. Invoke osv-scanner via `--lockfile=NuGet:<tmp>`.
|
|
438
|
+
* 6. Parse output via the shared `parseOsvScannerFindings` (same
|
|
439
|
+
* helper kotlin/java/ruby packs already use for their osv path).
|
|
440
|
+
* 7. Clean up temp file.
|
|
441
|
+
*
|
|
442
|
+
* Transitive coverage: NOT included. The direct-parsing path only
|
|
443
|
+
* surfaces vulnerabilities in packages explicitly listed in a .csproj's
|
|
444
|
+
* `<PackageReference>` blocks. Industry studies put ~80% of typical
|
|
445
|
+
* .NET CVE surface on direct refs; the remaining ~20% needs
|
|
446
|
+
* `project.assets.json` or `dotnet list --include-transitive` which
|
|
447
|
+
* the D025c path covers when available.
|
|
448
|
+
*
|
|
449
|
+
* Return shape: same `DepVulnGatherOutcome` as the primary gather.
|
|
450
|
+
* `unavailableReason` carries the original dotnet failure reason
|
|
451
|
+
* concatenated with the fallback's own failure (osv-scanner missing,
|
|
452
|
+
* no parseable references, etc.) so the customer can trace BOTH
|
|
453
|
+
* paths' state from a single message.
|
|
446
454
|
*/
|
|
447
|
-
async function
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
455
|
+
async function gatherDirectPackageReferenceFallback(cwd, dotnetFailureReason) {
|
|
456
|
+
const csprojs = findAllCsprojFiles(cwd);
|
|
457
|
+
if (csprojs.length === 0) {
|
|
458
|
+
// Shouldn't happen (hasCsharpProject already passed) but defensive
|
|
459
|
+
// in case the walk + the gate diverge somehow.
|
|
460
|
+
return {
|
|
461
|
+
kind: 'unavailable',
|
|
462
|
+
reason: `${dotnetFailureReason}; no .csproj files for direct-parsing fallback`,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
const scanner = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS['osv-scanner'], cwd);
|
|
466
|
+
if (!scanner.available || !scanner.path) {
|
|
467
|
+
return {
|
|
468
|
+
kind: 'unavailable',
|
|
469
|
+
reason: `${dotnetFailureReason}; osv-scanner also unavailable for D025f fallback`,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
// Aggregate PackageReferences across every .csproj. Cross-csproj
|
|
473
|
+
// dedup at the name+version level here; package-only collisions get
|
|
474
|
+
// resolved by `buildNugetAdhocLockfile`'s last-write-wins.
|
|
475
|
+
const entries = [];
|
|
476
|
+
const seen = new Set();
|
|
477
|
+
for (const csprojPath of csprojs) {
|
|
478
|
+
let xml;
|
|
479
|
+
try {
|
|
480
|
+
xml = fs.readFileSync(csprojPath, 'utf-8');
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
for (const entry of (0, nuget_package_reference_1.parseCsprojPackageReferences)(xml)) {
|
|
486
|
+
const key = `${entry.name}@${entry.version}`;
|
|
487
|
+
if (seen.has(key))
|
|
488
|
+
continue;
|
|
489
|
+
seen.add(key);
|
|
490
|
+
entries.push(entry);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (entries.length === 0) {
|
|
494
|
+
return {
|
|
495
|
+
kind: 'no-manifest',
|
|
496
|
+
reason: `${csprojs.length} .csproj files contain no parseable PackageReferences (possibly Central Package Management — Version comes from Directory.Packages.props which D025f doesn't yet resolve)`,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
// osv-scanner v2.x detects lockfile ecosystem by FILENAME — there's
|
|
500
|
+
// no `--lockfile=NuGet:<path>` syntax (verified 2026-05-12 during
|
|
501
|
+
// D025f integration; that syntax was speculation). The NuGet
|
|
502
|
+
// extractor only fires when the file is literally named
|
|
503
|
+
// `packages.lock.json`. We create a process-unique temp DIRECTORY
|
|
504
|
+
// and write the adhoc file inside it as `packages.lock.json`; the
|
|
505
|
+
// dir wraps the file so concurrent dxkit runs don't collide on a
|
|
506
|
+
// single `/tmp/packages.lock.json` path.
|
|
507
|
+
const adhocDir = fs.mkdtempSync(path.join(os.tmpdir(), `dxkit-nuget-adhoc-${process.pid}-`));
|
|
508
|
+
const adhocPath = path.join(adhocDir, 'packages.lock.json');
|
|
509
|
+
try {
|
|
510
|
+
fs.writeFileSync(adhocPath, (0, nuget_package_reference_1.buildNugetAdhocLockfile)(entries));
|
|
511
|
+
const raw = (0, runner_1.run)(`${scanner.path} scan source --lockfile=${adhocPath} --format json 2>/dev/null`, cwd, 180000);
|
|
512
|
+
if (!raw) {
|
|
513
|
+
return {
|
|
514
|
+
kind: 'unavailable',
|
|
515
|
+
reason: `${dotnetFailureReason}; D025f fallback ran but osv-scanner produced no output on ${entries.length} parsed PackageReferences`,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const { counts, findings, vulnsForCvss } = (0, osv_scanner_deps_1.parseOsvScannerFindings)(raw, 'NuGet', 'csharp');
|
|
519
|
+
// Per-finding CVSS enrichment — mirrors the primary csharp gather's
|
|
520
|
+
// OSV alias-fallback path. Direct PackageReferences carry the
|
|
521
|
+
// package name + version; osv-scanner emits the advisory IDs;
|
|
522
|
+
// resolveCvssScores attaches scores via GHSA → CVE alias chain.
|
|
523
|
+
if (findings.length > 0) {
|
|
524
|
+
const resolved = await (0, osv_1.resolveCvssScores)(vulnsForCvss);
|
|
525
|
+
for (const f of findings) {
|
|
526
|
+
const score = resolved.get(f.id);
|
|
527
|
+
if (score !== null && score !== undefined)
|
|
528
|
+
f.cvssScore = score;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
const envelope = {
|
|
532
|
+
schemaVersion: 1,
|
|
533
|
+
tool: 'osv-scanner-nuget-direct',
|
|
534
|
+
enrichment: 'osv.dev',
|
|
535
|
+
counts,
|
|
536
|
+
findings,
|
|
537
|
+
};
|
|
538
|
+
return { kind: 'success', envelope };
|
|
539
|
+
}
|
|
540
|
+
finally {
|
|
541
|
+
// Clean up the whole adhoc directory (file + dir). Best-effort —
|
|
542
|
+
// the OS will reap `/tmp/` on next boot if we miss it.
|
|
543
|
+
try {
|
|
544
|
+
fs.unlinkSync(adhocPath);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
/* best-effort */
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
fs.rmdirSync(adhocDir);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
/* best-effort */
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Run `dotnet list package --vulnerable --include-transitive` on cwd
|
|
559
|
+
* and return a DepVulnGatherOutcome. The "primary" half of the
|
|
560
|
+
* always-merge G_v4_9 strategy. Pulled out of
|
|
561
|
+
* `gatherCsharpDepVulnsResult` so both halves can run in parallel.
|
|
562
|
+
*
|
|
563
|
+
* Returns no-manifest for the dotnet-not-installed case (the
|
|
564
|
+
* dotnet-vulnerable path simply can't run); the fallback-half is
|
|
565
|
+
* the source of truth in that case.
|
|
566
|
+
*/
|
|
567
|
+
async function runDotnetVulnerablePath(cwd) {
|
|
568
|
+
const dotnet = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS['dotnet-format'], cwd);
|
|
569
|
+
if (!dotnet.available || !dotnet.path) {
|
|
570
|
+
return {
|
|
571
|
+
kind: 'no-manifest',
|
|
572
|
+
reason: 'dotnet SDK not installed (osv-scanner path covers this case)',
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
const vulnRaw = (0, runner_1.run)(`${dotnet.path} list package --vulnerable --include-transitive --format json 2>/dev/null`, cwd, 120000);
|
|
576
|
+
if (!vulnRaw) {
|
|
577
|
+
// D036 case: dotnet couldn't pick a project from this cwd (e.g.
|
|
578
|
+
// cwd is the repo root, the .csproj lives in a sub-dir). Treat
|
|
579
|
+
// as no-manifest at this layer; the always-merge wrapper relies
|
|
580
|
+
// on the osv-scanner-nuget-direct half to surface vulns instead.
|
|
581
|
+
return { kind: 'no-manifest', reason: 'dotnet list package produced no output (D036)' };
|
|
582
|
+
}
|
|
457
583
|
const parsed = parseDotnetVulnerableOutput(vulnRaw);
|
|
458
|
-
if (!parsed)
|
|
459
|
-
return { kind: 'parse
|
|
584
|
+
if (!parsed) {
|
|
585
|
+
return { kind: 'unavailable', reason: 'dotnet list package output failed JSON parse' };
|
|
586
|
+
}
|
|
460
587
|
const { counts, findings } = parsed;
|
|
461
588
|
// Attach top-level attribution to transitive findings (top-level
|
|
462
589
|
// findings already carry self-attribution from the parser). Skipped
|
|
463
590
|
// when project.assets.json is absent — user hasn't run
|
|
464
|
-
// `dotnet restore`, or the obj/ dir was cleaned.
|
|
465
|
-
// `topLevelDep` unset in that case, matching Python's no-venv path.
|
|
591
|
+
// `dotnet restore`, or the obj/ dir was cleaned.
|
|
466
592
|
const transitiveNeedsAttribution = findings.some((f) => !f.topLevelDep || f.topLevelDep.length === 0);
|
|
467
593
|
if (transitiveNeedsAttribution) {
|
|
468
594
|
const topLevelIndex = loadCsharpTopLevelDepIndex(cwd);
|
|
@@ -476,56 +602,149 @@ async function gatherCsharpDepVulnsResult(cwd) {
|
|
|
476
602
|
}
|
|
477
603
|
}
|
|
478
604
|
}
|
|
605
|
+
const envelope = {
|
|
606
|
+
schemaVersion: 1,
|
|
607
|
+
tool: 'dotnet-vulnerable',
|
|
608
|
+
enrichment: null,
|
|
609
|
+
counts,
|
|
610
|
+
findings,
|
|
611
|
+
};
|
|
612
|
+
return { kind: 'success', envelope };
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Single source of truth for the csharp pack's dep-vuln gathering.
|
|
616
|
+
* Consumed by `csharpDepVulnsProvider` (capability dispatcher).
|
|
617
|
+
*
|
|
618
|
+
* G_v4_9 (2.4.7 Phase C1.9) always-merge strategy:
|
|
619
|
+
*
|
|
620
|
+
* **Both tools run, results merge by fingerprint** when each is
|
|
621
|
+
* available. The csharp pack's gather is now cwd-invariant: the
|
|
622
|
+
* same fingerprint set is produced regardless of where cwd points
|
|
623
|
+
* within the repo. Pre-G_v4_9 the gather was cwd-sensitive (D107):
|
|
624
|
+
* at sub-roots with stale `obj/project.assets.json`, dotnet
|
|
625
|
+
* returned 0 findings and the safety-net fallback didn't fire
|
|
626
|
+
* (its condition required no project.assets.json); at repo-root,
|
|
627
|
+
* dotnet returned no output (D036) and the fallback fired correctly,
|
|
628
|
+
* surfacing 2 NuGet CVEs on dpl-studio. Different cwd, different
|
|
629
|
+
* totals — same disease class as D086/D087/D091 at the pack-contract
|
|
630
|
+
* layer instead of the consumer layer.
|
|
631
|
+
*
|
|
632
|
+
* **dotnet path** (`dotnet list package --vulnerable --include-transitive`):
|
|
633
|
+
* surfaces transitive vulns via NuGet's own resolution when
|
|
634
|
+
* `dotnet restore` has been run.
|
|
635
|
+
*
|
|
636
|
+
* **osv-scanner-nuget-direct path**: parses every `.csproj` for
|
|
637
|
+
* direct PackageReferences, writes an adhoc `packages.lock.json`,
|
|
638
|
+
* runs osv-scanner. Covers ~80% of typical .NET CVE surface
|
|
639
|
+
* (direct refs only — no transitive resolution).
|
|
640
|
+
*
|
|
641
|
+
* Findings union, fingerprint-deduped at (package, installedVersion,
|
|
642
|
+
* id). Envelope counts recomputed from the merged set. Both tool
|
|
643
|
+
* names join in the envelope's `tool` field so users see what
|
|
644
|
+
* actually ran.
|
|
645
|
+
*
|
|
646
|
+
* Manifest gate: a `.csproj` or `.sln` must be findable within depth 5
|
|
647
|
+
* (D035 / `hasCsharpProject`). Below that, the gather doesn't even
|
|
648
|
+
* try — that's a `no-manifest` outcome.
|
|
649
|
+
*/
|
|
650
|
+
async function gatherCsharpDepVulnsResult(cwd) {
|
|
651
|
+
if (!hasCsharpProject(cwd)) {
|
|
652
|
+
return { kind: 'no-manifest', reason: 'no .csproj or .sln found within depth 5' };
|
|
653
|
+
}
|
|
654
|
+
// Run both tools in parallel. Each returns a DepVulnGatherOutcome;
|
|
655
|
+
// we merge whatever succeeds.
|
|
656
|
+
const [primaryOutcome, fallbackOutcome] = await Promise.all([
|
|
657
|
+
runDotnetVulnerablePath(cwd),
|
|
658
|
+
gatherDirectPackageReferenceFallback(cwd, 'G_v4_9 always-merge'),
|
|
659
|
+
]);
|
|
660
|
+
// Pick the better outcome when both fail to succeed.
|
|
661
|
+
if (primaryOutcome.kind !== 'success' && fallbackOutcome.kind !== 'success') {
|
|
662
|
+
// Surface the more-informative reason. `unavailable` (tool ran
|
|
663
|
+
// and failed) beats `no-manifest` (tool didn't apply).
|
|
664
|
+
if (primaryOutcome.kind === 'unavailable')
|
|
665
|
+
return primaryOutcome;
|
|
666
|
+
if (fallbackOutcome.kind === 'unavailable')
|
|
667
|
+
return fallbackOutcome;
|
|
668
|
+
// Both no-manifest → genuinely nothing to scan.
|
|
669
|
+
return {
|
|
670
|
+
kind: 'no-manifest',
|
|
671
|
+
reason: 'csharp dep-vuln scan unavailable on this cwd: both dotnet and osv-scanner-nuget-direct returned no-manifest',
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
// Merge findings from whichever succeeded. Dedup at the pack layer
|
|
675
|
+
// by (package, installedVersion, id) so envelope counts are honest;
|
|
676
|
+
// downstream aggregator also fingerprints to be safe.
|
|
677
|
+
const primaryFindings = primaryOutcome.kind === 'success' ? (primaryOutcome.envelope.findings ?? []) : [];
|
|
678
|
+
const fallbackFindings = fallbackOutcome.kind === 'success' ? (fallbackOutcome.envelope.findings ?? []) : [];
|
|
679
|
+
const merged = [];
|
|
680
|
+
const seen = new Set();
|
|
681
|
+
for (const f of [...primaryFindings, ...fallbackFindings]) {
|
|
682
|
+
const key = `${f.package}\0${f.installedVersion ?? ''}\0${f.id}`;
|
|
683
|
+
if (seen.has(key))
|
|
684
|
+
continue;
|
|
685
|
+
seen.add(key);
|
|
686
|
+
merged.push(f);
|
|
687
|
+
}
|
|
479
688
|
// Alias-fallback CVSS pass: dotnet --vulnerable ships zero CVSS data
|
|
480
689
|
// per advisory; the GHSA id (extracted from advisoryUrl) is the only
|
|
481
690
|
// anchor. resolveCvssScores looks up via GHSA → CVE alias chain.
|
|
482
|
-
|
|
483
|
-
|
|
691
|
+
// Run on the merged set so fallback findings also get enriched if
|
|
692
|
+
// dotnet's path didn't carry them.
|
|
693
|
+
if (merged.length > 0) {
|
|
694
|
+
const cvssInputs = merged.map((f) => ({
|
|
484
695
|
primaryId: f.id,
|
|
485
696
|
embeddedCvss: f.cvssScore ?? null,
|
|
486
697
|
aliases: f.aliases ?? [],
|
|
487
698
|
}));
|
|
488
699
|
const resolved = await (0, osv_1.resolveCvssScores)(cvssInputs);
|
|
489
|
-
for (const f of
|
|
700
|
+
for (const f of merged) {
|
|
490
701
|
const score = resolved.get(f.id);
|
|
491
702
|
if (score !== null && score !== undefined)
|
|
492
703
|
f.cvssScore = score;
|
|
493
704
|
}
|
|
494
705
|
}
|
|
706
|
+
// Recompute counts from merged set so the envelope is internally
|
|
707
|
+
// consistent (pre-G_v4_9 each path computed its own counts; sum
|
|
708
|
+
// would double-count overlapping advisories). aggregator-ok: this
|
|
709
|
+
// builds the pack's DepVulnResult envelope from its OWN findings,
|
|
710
|
+
// which is the dispatcher input — distinct from consumer-side
|
|
711
|
+
// re-aggregation across envelopes that G_v4_8 prohibits.
|
|
712
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
713
|
+
for (const f of merged)
|
|
714
|
+
counts[f.severity]++; // aggregator-ok: pack envelope counts from own merged findings
|
|
715
|
+
// Compose envelope tool names from what actually succeeded.
|
|
716
|
+
const tools = [];
|
|
717
|
+
if (primaryOutcome.kind === 'success')
|
|
718
|
+
tools.push(primaryOutcome.envelope.tool);
|
|
719
|
+
if (fallbackOutcome.kind === 'success')
|
|
720
|
+
tools.push(fallbackOutcome.envelope.tool);
|
|
721
|
+
const enrichment = fallbackOutcome.kind === 'success' ? fallbackOutcome.envelope.enrichment : null;
|
|
495
722
|
const envelope = {
|
|
496
723
|
schemaVersion: 1,
|
|
497
|
-
tool: '
|
|
498
|
-
enrichment
|
|
724
|
+
tool: tools.join(', '),
|
|
725
|
+
enrichment,
|
|
499
726
|
counts,
|
|
500
|
-
findings,
|
|
727
|
+
findings: merged,
|
|
501
728
|
};
|
|
502
729
|
return { kind: 'success', envelope };
|
|
503
730
|
}
|
|
504
|
-
/**
|
|
505
|
-
*
|
|
731
|
+
/**
|
|
732
|
+
* True if `cwd` (or any nested directory up to depth 5) contains a
|
|
733
|
+
* `.csproj` or `.sln` file. Used as the depVulns gather preflight —
|
|
734
|
+
* `dotnet list package --vulnerable` needs a project file in scope.
|
|
735
|
+
*
|
|
736
|
+
* Depth 5 mirrors `csharp.detect()`'s recursive walk (D024 / 2.4.7):
|
|
737
|
+
* enterprise layouts like dpl-studio nest .csproj under
|
|
738
|
+
* `Code/Source/Dev/Core/<Module>/`. Pre-D035, this was a depth-1
|
|
739
|
+
* custom walk; symmetrical with detect()'s old depth-3 limit until
|
|
740
|
+
* D024 broke the symmetry. The Sprint A dpl-studio validation
|
|
741
|
+
* (2026-05-12) surfaced the inconsistency — D025c's tool-registry
|
|
742
|
+
* probe was unreachable from `Code/Source/` because this preflight
|
|
743
|
+
* rejected before reaching it. See D035 in tmp/known-defects.md.
|
|
744
|
+
*/
|
|
506
745
|
function hasCsharpProject(cwd) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
510
|
-
}
|
|
511
|
-
catch {
|
|
512
|
-
return false;
|
|
513
|
-
}
|
|
514
|
-
for (const e of entries) {
|
|
515
|
-
if (e.isFile() && (e.name.endsWith('.csproj') || e.name.endsWith('.sln')))
|
|
516
|
-
return true;
|
|
517
|
-
if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules') {
|
|
518
|
-
try {
|
|
519
|
-
const sub = fs.readdirSync(path.join(cwd, e.name), { withFileTypes: true });
|
|
520
|
-
if (sub.some((s) => s.isFile() && (s.name.endsWith('.csproj') || s.name.endsWith('.sln'))))
|
|
521
|
-
return true;
|
|
522
|
-
}
|
|
523
|
-
catch {
|
|
524
|
-
/* unreadable subdir — ignore */
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return false;
|
|
746
|
+
return (dirHasMatching(cwd, /\.(sln|csproj)$/) ||
|
|
747
|
+
(0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj', '.sln'] }).length > 0);
|
|
529
748
|
}
|
|
530
749
|
const csharpDepVulnsProvider = {
|
|
531
750
|
source: 'csharp',
|
|
@@ -533,6 +752,9 @@ const csharpDepVulnsProvider = {
|
|
|
533
752
|
const outcome = await gatherCsharpDepVulnsResult(cwd);
|
|
534
753
|
return outcome.kind === 'success' ? outcome.envelope : null;
|
|
535
754
|
},
|
|
755
|
+
async gatherOutcome(cwd) {
|
|
756
|
+
return gatherCsharpDepVulnsResult(cwd);
|
|
757
|
+
},
|
|
536
758
|
};
|
|
537
759
|
/**
|
|
538
760
|
* Single source of truth for the csharp pack's lint gathering.
|
|
@@ -599,11 +821,56 @@ function gatherCsharpCoverageResult(cwd) {
|
|
|
599
821
|
return null;
|
|
600
822
|
return { schemaVersion: 1, tool: `coverage:${coverage.source}`, coverage };
|
|
601
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Run `dotnet test --collect:"XPlat Code Coverage"` from cwd (D021).
|
|
826
|
+
*
|
|
827
|
+
* `dotnet test --collect` is the canonical way to materialize coverage
|
|
828
|
+
* across the whole solution. It auto-discovers test projects and writes
|
|
829
|
+
* a cobertura XML into `TestResults/<guid>/coverage.cobertura.xml`,
|
|
830
|
+
* where `<guid>` is generated per test run. The artifact param uses
|
|
831
|
+
* the function form so the helper locates the actual file post-run via
|
|
832
|
+
* the existing `findCoberturaArtifact` (which already knows the
|
|
833
|
+
* TestResults layout and the explicit `coverage/coverage.cobertura.xml`
|
|
834
|
+
* fallback).
|
|
835
|
+
*
|
|
836
|
+
* `--results-directory TestResults` pins the parent directory so
|
|
837
|
+
* `findCoberturaArtifact` doesn't have to walk arbitrary roots — the
|
|
838
|
+
* GUID-named subdirectory underneath is still non-deterministic but
|
|
839
|
+
* bounded.
|
|
840
|
+
*
|
|
841
|
+
* Preflight: require a `.csproj` or `.sln` in cwd. Without one, this
|
|
842
|
+
* isn't a .NET project root. We don't preflight `dotnet` itself — the
|
|
843
|
+
* spawn ENOENT path classifies it as `unavailable` cleanly.
|
|
844
|
+
*/
|
|
845
|
+
function runCsharpTestsWithCoverage(cwd) {
|
|
846
|
+
return Promise.resolve((0, run_tests_helper_1.runTestsWithCoverage)({
|
|
847
|
+
pack: 'csharp',
|
|
848
|
+
cmd: 'dotnet test --collect:"XPlat Code Coverage" --results-directory TestResults',
|
|
849
|
+
cwd,
|
|
850
|
+
artifact: (cwd) => {
|
|
851
|
+
const abs = findCoberturaArtifact(cwd);
|
|
852
|
+
if (!abs)
|
|
853
|
+
return null;
|
|
854
|
+
return path.relative(cwd, abs).split(path.sep).join('/');
|
|
855
|
+
},
|
|
856
|
+
preflight: (cwd) => {
|
|
857
|
+
const hasManifest = dirHasMatching(cwd, /\.(sln|csproj)$/i) ||
|
|
858
|
+
(0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj', '.sln'] }).length > 0;
|
|
859
|
+
if (!hasManifest) {
|
|
860
|
+
return 'no .csproj or .sln in this directory tree — not a .NET project';
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
},
|
|
864
|
+
}));
|
|
865
|
+
}
|
|
602
866
|
const csharpCoverageProvider = {
|
|
603
867
|
source: 'csharp',
|
|
604
868
|
async gather(cwd) {
|
|
605
869
|
return gatherCsharpCoverageResult(cwd);
|
|
606
870
|
},
|
|
871
|
+
async runTests(cwd) {
|
|
872
|
+
return runCsharpTestsWithCoverage(cwd);
|
|
873
|
+
},
|
|
607
874
|
};
|
|
608
875
|
/**
|
|
609
876
|
* Capture C# `using` directives from source text, including
|
|
@@ -669,7 +936,7 @@ const csharpImportsProvider = {
|
|
|
669
936
|
* `.csproj` referencing these returns null.
|
|
670
937
|
*/
|
|
671
938
|
function gatherCsharpTestFrameworkResult(cwd) {
|
|
672
|
-
const hasCsproj = (0, runner_1.fileExists)(cwd, '*.csproj') ||
|
|
939
|
+
const hasCsproj = (0, runner_1.fileExists)(cwd, '*.csproj') || (0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj'] }).length > 0;
|
|
673
940
|
if (!hasCsproj)
|
|
674
941
|
return null;
|
|
675
942
|
const csproj = (0, runner_1.run)("find . -name '*.csproj' -exec grep -l 'xunit\\|nunit\\|MSTest' {} \\; 2>/dev/null | head -1", cwd);
|
|
@@ -690,19 +957,23 @@ const csharpTestFrameworkProvider = {
|
|
|
690
957
|
* or null — callers skip cleanly on null.
|
|
691
958
|
*/
|
|
692
959
|
function findCsharpLicenseInput(cwd) {
|
|
693
|
-
//
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
960
|
+
// Prefer a `.sln` (covers every csproj in the solution in one
|
|
961
|
+
// nuget-license pass) over a single `.csproj`. Within each kind,
|
|
962
|
+
// pick the shallowest match — closest to repo root is the most
|
|
963
|
+
// likely "primary" manifest. Depth-unlimited via the canonical
|
|
964
|
+
// walker, so deep monorepos (manifests 6–9 levels under repo root)
|
|
965
|
+
// discover them where prior hard depth caps silently missed.
|
|
966
|
+
const manifests = (0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj', '.sln'] });
|
|
967
|
+
if (manifests.length === 0)
|
|
699
968
|
return null;
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
969
|
+
manifests.sort((a, b) => {
|
|
970
|
+
const aIsSln = a.endsWith('.sln') ? 0 : 1;
|
|
971
|
+
const bIsSln = b.endsWith('.sln') ? 0 : 1;
|
|
972
|
+
if (aIsSln !== bIsSln)
|
|
973
|
+
return aIsSln - bIsSln;
|
|
974
|
+
return a.split('/').length - b.split('/').length;
|
|
975
|
+
});
|
|
976
|
+
return path.join(cwd, manifests[0]);
|
|
706
977
|
}
|
|
707
978
|
/**
|
|
708
979
|
* Single source of truth for the csharp pack's license gathering.
|
|
@@ -714,25 +985,116 @@ function findCsharpLicenseInput(cwd) {
|
|
|
714
985
|
* tool, wrapped. Returns null cleanly when no .sln/.csproj is present
|
|
715
986
|
* or when the tool isn't installed.
|
|
716
987
|
*/
|
|
988
|
+
/**
|
|
989
|
+
* D031-2 (2.4.7) — degraded license inventory fallback.
|
|
990
|
+
*
|
|
991
|
+
* When `nuget-license` is unavailable but we have `.csproj` files,
|
|
992
|
+
* parse direct PackageReferences from each .csproj and emit a
|
|
993
|
+
* LicensesResult with `licenseType: 'UNKNOWN'` per package — name +
|
|
994
|
+
* version only. Reuses the D025f-1 `parseCsprojPackageReferences`
|
|
995
|
+
* helper (and `findAllCsprojFiles` walker) so this fallback shares
|
|
996
|
+
* one truth source with the depVulns direct-scan path.
|
|
997
|
+
*
|
|
998
|
+
* Customer outcome on dpl-studio: pre-D031 → "0 packages" (with the
|
|
999
|
+
* pack reporting `unavailable` because `nuget-license` is missing).
|
|
1000
|
+
* Post-D031 → "53 packages identified; license info unavailable" via
|
|
1001
|
+
* the markdown framing banner from `analyzers/licenses/index.ts`.
|
|
1002
|
+
* The customer can decide remediation BEFORE installing nuget-license
|
|
1003
|
+
* (e.g., "we have 53 NuGet deps; install nuget-license to see their
|
|
1004
|
+
* licenses" is more actionable than "0 packages").
|
|
1005
|
+
*
|
|
1006
|
+
* Returns `null` when the degraded path itself can't produce data
|
|
1007
|
+
* (no .csproj files at all, or every .csproj has zero parseable
|
|
1008
|
+
* PackageReferences). Caller propagates the original `unavailable`
|
|
1009
|
+
* outcome in those cases.
|
|
1010
|
+
*/
|
|
1011
|
+
function gatherCsharpLicensesDegradedInventory(cwd) {
|
|
1012
|
+
const csprojs = findAllCsprojFiles(cwd);
|
|
1013
|
+
if (csprojs.length === 0)
|
|
1014
|
+
return null;
|
|
1015
|
+
const entries = [];
|
|
1016
|
+
const seen = new Set();
|
|
1017
|
+
for (const csprojPath of csprojs) {
|
|
1018
|
+
let xml;
|
|
1019
|
+
try {
|
|
1020
|
+
xml = fs.readFileSync(csprojPath, 'utf-8');
|
|
1021
|
+
}
|
|
1022
|
+
catch {
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
for (const entry of (0, nuget_package_reference_1.parseCsprojPackageReferences)(xml)) {
|
|
1026
|
+
const key = `${entry.name}@${entry.version}`;
|
|
1027
|
+
if (seen.has(key))
|
|
1028
|
+
continue;
|
|
1029
|
+
seen.add(key);
|
|
1030
|
+
entries.push(entry);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (entries.length === 0)
|
|
1034
|
+
return null;
|
|
1035
|
+
const findings = entries.map((e) => ({
|
|
1036
|
+
package: e.name,
|
|
1037
|
+
version: e.version,
|
|
1038
|
+
licenseType: 'UNKNOWN',
|
|
1039
|
+
// No license URL / description / supplier — the degraded path only
|
|
1040
|
+
// has the manifest reference. Customer can identify what's
|
|
1041
|
+
// installed; install nuget-license for the full inventory.
|
|
1042
|
+
}));
|
|
1043
|
+
return {
|
|
1044
|
+
schemaVersion: 1,
|
|
1045
|
+
tool: 'csharp-package-reference-degraded',
|
|
1046
|
+
findings,
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
717
1049
|
function gatherCsharpLicensesResult(cwd) {
|
|
718
1050
|
const input = findCsharpLicenseInput(cwd);
|
|
719
|
-
if (!input)
|
|
720
|
-
return
|
|
1051
|
+
if (!input) {
|
|
1052
|
+
return { kind: 'no-manifest', reason: 'no .csproj or .sln found within depth 3' };
|
|
1053
|
+
}
|
|
721
1054
|
const status = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS['nuget-license'], cwd);
|
|
722
|
-
if (!status.available || !status.path)
|
|
723
|
-
|
|
1055
|
+
if (!status.available || !status.path) {
|
|
1056
|
+
// D031-2: degraded-inventory fallback. nuget-license is the
|
|
1057
|
+
// canonical tool but parsing direct PackageReferences gives the
|
|
1058
|
+
// customer at minimum a name+version inventory while the tool
|
|
1059
|
+
// is being installed. We surface this through the SUCCESS path
|
|
1060
|
+
// (envelope's tool name carries the `-degraded` suffix so
|
|
1061
|
+
// downstream renderers can disambiguate) so the LicensesReport's
|
|
1062
|
+
// summary count reflects real data — the framing banner from
|
|
1063
|
+
// `analyzers/licenses/index.ts` explains the asterisk.
|
|
1064
|
+
//
|
|
1065
|
+
// Path B (no .csproj or empty PackageReferences across all
|
|
1066
|
+
// csprojs): degraded inventory returns null; we propagate the
|
|
1067
|
+
// original `unavailable` so the report frames the gap honestly
|
|
1068
|
+
// ("0 packages" with the explanatory ⚠ banner).
|
|
1069
|
+
const degraded = gatherCsharpLicensesDegradedInventory(cwd);
|
|
1070
|
+
if (degraded)
|
|
1071
|
+
return { kind: 'success', envelope: degraded };
|
|
1072
|
+
return { kind: 'unavailable', reason: 'nuget-license not installed' };
|
|
1073
|
+
}
|
|
724
1074
|
const raw = (0, runner_1.run)(`${status.path} -i "${input}" -o JsonPretty 2>/dev/null`, cwd, 180000);
|
|
725
|
-
if (!raw)
|
|
726
|
-
|
|
1075
|
+
if (!raw) {
|
|
1076
|
+
// D031-2 extended (2.4.7): nuget-license commonly produces no
|
|
1077
|
+
// output when `dotnet restore` hasn't been run (no
|
|
1078
|
+
// `obj/project.assets.json` for it to read). Rather than tell
|
|
1079
|
+
// the customer "0 packages," fall back to direct PackageReference
|
|
1080
|
+
// parsing so they get a real inventory. Validated on dpl-studio
|
|
1081
|
+
// 2026-05-13: 53 packages surface via the fallback when
|
|
1082
|
+
// nuget-license itself returns empty.
|
|
1083
|
+
const degraded = gatherCsharpLicensesDegradedInventory(cwd);
|
|
1084
|
+
if (degraded)
|
|
1085
|
+
return { kind: 'success', envelope: degraded };
|
|
1086
|
+
return { kind: 'unavailable', reason: 'nuget-license produced no output' };
|
|
1087
|
+
}
|
|
727
1088
|
let data;
|
|
728
1089
|
try {
|
|
729
1090
|
data = JSON.parse(raw);
|
|
730
1091
|
}
|
|
731
|
-
catch {
|
|
732
|
-
return
|
|
1092
|
+
catch (err) {
|
|
1093
|
+
return { kind: 'unavailable', reason: `nuget-license parse error: ${err.message}` };
|
|
1094
|
+
}
|
|
1095
|
+
if (!Array.isArray(data)) {
|
|
1096
|
+
return { kind: 'unavailable', reason: 'nuget-license output was not a JSON array' };
|
|
733
1097
|
}
|
|
734
|
-
if (!Array.isArray(data))
|
|
735
|
-
return null;
|
|
736
1098
|
// Top-level attribution reuses project.assets.json via
|
|
737
1099
|
// loadCsharpTopLevelDepIndex. Same self-parent invariant as the
|
|
738
1100
|
// other packs. Missing assets-json (user hasn't run dotnet restore)
|
|
@@ -757,15 +1119,34 @@ function gatherCsharpLicensesResult(cwd) {
|
|
|
757
1119
|
isTopLevel: hasIndex ? (parents?.includes(entry.PackageId) ?? false) : undefined,
|
|
758
1120
|
});
|
|
759
1121
|
}
|
|
760
|
-
|
|
1122
|
+
// D031-2 extended (2.4.7): if nuget-license ran AND parsed cleanly
|
|
1123
|
+
// but produced zero valid findings, the canonical scan failed
|
|
1124
|
+
// silently (typically: assets.json absent → tool emits an empty
|
|
1125
|
+
// array). Same customer-visibility concern as the no-output path
|
|
1126
|
+
// — fall back to degraded inventory so the customer sees real
|
|
1127
|
+
// packages instead of "0 packages." If the degraded path also
|
|
1128
|
+
// returns null (no .csproj parseable), drop through to the
|
|
1129
|
+
// empty-success path (legitimate "no licenseable deps in this
|
|
1130
|
+
// pack's scope").
|
|
1131
|
+
if (findings.length === 0) {
|
|
1132
|
+
const degraded = gatherCsharpLicensesDegradedInventory(cwd);
|
|
1133
|
+
if (degraded)
|
|
1134
|
+
return { kind: 'success', envelope: degraded };
|
|
1135
|
+
}
|
|
1136
|
+
const envelope = {
|
|
761
1137
|
schemaVersion: 1,
|
|
762
1138
|
tool: 'nuget-license',
|
|
763
1139
|
findings,
|
|
764
1140
|
};
|
|
1141
|
+
return { kind: 'success', envelope };
|
|
765
1142
|
}
|
|
766
1143
|
const csharpLicensesProvider = {
|
|
767
1144
|
source: 'csharp',
|
|
768
1145
|
async gather(cwd) {
|
|
1146
|
+
const outcome = gatherCsharpLicensesResult(cwd);
|
|
1147
|
+
return outcome.kind === 'success' ? outcome.envelope : null;
|
|
1148
|
+
},
|
|
1149
|
+
async gatherOutcome(cwd) {
|
|
769
1150
|
return gatherCsharpLicensesResult(cwd);
|
|
770
1151
|
},
|
|
771
1152
|
};
|
|
@@ -777,10 +1158,107 @@ exports.csharp = {
|
|
|
777
1158
|
// because gather.ts only matched *.test.*, *.spec.*, *_test.*, test_*.
|
|
778
1159
|
testFilePatterns: ['*Tests.cs', '*.Tests.cs'],
|
|
779
1160
|
extraExcludes: ['bin', 'obj', 'TestResults', 'packages'],
|
|
1161
|
+
// D028 (2.4.7): auto-generated .NET source patterns. Visual Studio's
|
|
1162
|
+
// WinForms designer creates `*.designer.cs` (UI scaffolding,
|
|
1163
|
+
// typically hundreds of lines, repetitive). T4 templates emit
|
|
1164
|
+
// `*.g.cs`. Source generators (Roslyn) emit `*.g.i.cs`. The legacy
|
|
1165
|
+
// AssemblyInfo split files (`*.AssemblyInfo.cs`,
|
|
1166
|
+
// `*.AssemblyAttributes.cs`) are MSBuild-generated. None of these
|
|
1167
|
+
// are human-authored; counting them as "source" inflates Code
|
|
1168
|
+
// Quality + Maintainability dimensions on any .NET UI codebase
|
|
1169
|
+
// (dpl-studio is the motivating case — large WinForms enterprise
|
|
1170
|
+
// app with extensive designer.cs files).
|
|
1171
|
+
autogeneratedSourcePatterns: [
|
|
1172
|
+
'*.designer.cs',
|
|
1173
|
+
'*.Designer.cs',
|
|
1174
|
+
'*.g.cs',
|
|
1175
|
+
'*.g.i.cs',
|
|
1176
|
+
'*.generated.cs',
|
|
1177
|
+
'*.AssemblyInfo.cs',
|
|
1178
|
+
'*.AssemblyAttributes.cs',
|
|
1179
|
+
// WCF "Connected Services" auto-generated proxy classes
|
|
1180
|
+
// (svcutil.exe / Add Service Reference). dpl-studio's
|
|
1181
|
+
// `Reference.cs` was 42,370 lines of WCF proxy. Other tools'
|
|
1182
|
+
// service-reference output (gRPC tools, OpenAPI generators)
|
|
1183
|
+
// commonly also emit `Reference.cs`.
|
|
1184
|
+
'Reference.cs',
|
|
1185
|
+
],
|
|
1186
|
+
// D027 (2.4.7): C# uses XML-doc triple-slash comments above
|
|
1187
|
+
// public APIs. Pre-D027 the generic doc-comment heuristic
|
|
1188
|
+
// matched JSDoc `/**` only, so dpl-studio's 3,234 .cs files
|
|
1189
|
+
// contributed zero to docCommentFiles even though many carry
|
|
1190
|
+
// `/// <summary>` blocks. POSIX class `[[:space:]]` for cross-
|
|
1191
|
+
// platform grep (BSD vs GNU).
|
|
1192
|
+
docCommentPatterns: ['^[[:space:]]*///'],
|
|
1193
|
+
// D034 (2.4.7): canonical .NET TLS-bypass idioms. Pre-D034 only
|
|
1194
|
+
// Node-shaped (`NODE_TLS_REJECT_UNAUTHORIZED`) and Python-shaped
|
|
1195
|
+
// (`VERIFY_SSL`) tokens were grep'd — every C# project reported
|
|
1196
|
+
// zero TLS-bypass findings even when using
|
|
1197
|
+
// `ServerCertificateValidationCallback = (s,c,ch,e) => true` (the
|
|
1198
|
+
// canonical "accept any certificate" pattern). dpl-studio is the
|
|
1199
|
+
// motivating case: WCF + enterprise integration code commonly
|
|
1200
|
+
// ships permissive callbacks.
|
|
1201
|
+
//
|
|
1202
|
+
// Listed tokens:
|
|
1203
|
+
// - ServicePointManager.ServerCertificateValidationCallback (legacy)
|
|
1204
|
+
// - HttpClientHandler.ServerCertificateCustomValidationCallback
|
|
1205
|
+
// (HttpClient — .NET Core / 5+)
|
|
1206
|
+
// - SslStream.AuthenticateAsClient* with RemoteCertificateValidationCallback
|
|
1207
|
+
// - HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
|
1208
|
+
// (.NET 5+ shorthand — literally named `Dangerous*`)
|
|
1209
|
+
tlsBypassPatterns: [
|
|
1210
|
+
'ServerCertificateValidationCallback',
|
|
1211
|
+
'ServerCertificateCustomValidationCallback',
|
|
1212
|
+
'RemoteCertificateValidationCallback',
|
|
1213
|
+
'DangerousAcceptAnyServerCertificateValidator',
|
|
1214
|
+
],
|
|
1215
|
+
upgradeCommand(name, version) {
|
|
1216
|
+
return `dotnet add package ${name} --version ${version}`;
|
|
1217
|
+
},
|
|
1218
|
+
// .NET spans two very different application shapes: ASP.NET MVC /
|
|
1219
|
+
// Web API (controllers, endpoints) and WinForms / WPF desktop
|
|
1220
|
+
// (Forms, ViewModels, UserControls). Both are first-class
|
|
1221
|
+
// contributors here — a WinForms enterprise app's primary
|
|
1222
|
+
// architecture IS its Forms + Services layer, not a notional
|
|
1223
|
+
// controllers/ directory that doesn't exist on disk. The narrower
|
|
1224
|
+
// routePaths subset stays silent on desktop-only repos so the
|
|
1225
|
+
// "Add API documentation" recommendation doesn't fire there.
|
|
1226
|
+
architecturalShape: {
|
|
1227
|
+
primaryComponentPaths: [
|
|
1228
|
+
'/Controllers/',
|
|
1229
|
+
'/Services/',
|
|
1230
|
+
'/Forms/',
|
|
1231
|
+
'/ViewModels/',
|
|
1232
|
+
'/Pages/',
|
|
1233
|
+
'/Views/',
|
|
1234
|
+
'/UserControls/',
|
|
1235
|
+
'/Handlers/',
|
|
1236
|
+
],
|
|
1237
|
+
routePaths: ['/Controllers/', '/Endpoints/'],
|
|
1238
|
+
modelPaths: ['/Models/', '/Entities/', '/DTOs/', '/DataTransferObjects/', '/Domain/'],
|
|
1239
|
+
vocabulary: {
|
|
1240
|
+
components: 'Forms/Services',
|
|
1241
|
+
models: 'models',
|
|
1242
|
+
routes: 'endpoints',
|
|
1243
|
+
},
|
|
1244
|
+
testGapPriority: {
|
|
1245
|
+
high: ['/Controllers/', '/Services/', '/Handlers/'],
|
|
1246
|
+
medium: ['/Forms/', '/ViewModels/', '/Pages/', '/Views/', '/UserControls/'],
|
|
1247
|
+
},
|
|
1248
|
+
},
|
|
1249
|
+
clocLanguageNames: ['C#'],
|
|
780
1250
|
detect(cwd) {
|
|
781
|
-
|
|
1251
|
+
// Depth 5 covers enterprise .NET layouts like
|
|
1252
|
+
// `Code/Source/Dev/Core/<Module>/<Module>.csproj` (D024 / dpl-studio).
|
|
1253
|
+
// Lower limits silently miss these from the repo root.
|
|
1254
|
+
return (dirHasMatching(cwd, /\.(sln|csproj)$/) ||
|
|
1255
|
+
(0, walk_paths_1.walkPaths)(cwd, { extensions: ['.csproj', '.sln'] }).length > 0);
|
|
782
1256
|
},
|
|
783
|
-
|
|
1257
|
+
// D025f (2.4.7): osv-scanner added — used by the direct-PackageReference
|
|
1258
|
+
// fallback in gatherDirectPackageReferenceFallback when `dotnet list
|
|
1259
|
+
// package` can't produce output (D036). Cross-pack tool; kotlin/java/ruby
|
|
1260
|
+
// already use it via the shared osv-scanner-deps helper.
|
|
1261
|
+
tools: ['dotnet-format', 'nuget-license', 'osv-scanner'],
|
|
784
1262
|
// p/csharp semgrep ruleset is sparse — skip until it matures.
|
|
785
1263
|
semgrepRulesets: [],
|
|
786
1264
|
capabilities: {
|