hackmyagent 0.17.6 → 0.17.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/dist/.integrity-manifest.json +1 -1
- package/dist/cli.js +216 -17
- package/dist/cli.js.map +1 -1
- package/dist/hardening/scanner.d.ts +7 -0
- package/dist/hardening/scanner.d.ts.map +1 -1
- package/dist/hardening/scanner.js +17 -2
- package/dist/hardening/scanner.js.map +1 -1
- package/dist/nanomind-core/orchestrate.js +19 -5
- package/dist/nanomind-core/orchestrate.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -247,6 +247,7 @@ Examples:
|
|
|
247
247
|
},
|
|
248
248
|
verbose: !!options.verbose,
|
|
249
249
|
usedAnalm: !!options.analm,
|
|
250
|
+
analystFindings: nmResult.analystFindings,
|
|
250
251
|
});
|
|
251
252
|
const risk = critical.length > 0 ? 'critical' : high.length > 0 ? 'high' : issues.length > 0 ? 'medium' : 'low';
|
|
252
253
|
if (risk === 'critical' || risk === 'high')
|
|
@@ -773,6 +774,81 @@ function displayUnifiedCheck(opts) {
|
|
|
773
774
|
console.log(` ${colors.dim}${legend}${RESET()}`);
|
|
774
775
|
}
|
|
775
776
|
}
|
|
777
|
+
// ── AnaLM Analysis ──────────────────────────────────────────────────
|
|
778
|
+
if (opts.analystFindings && opts.analystFindings.length > 0) {
|
|
779
|
+
divider('AnaLM Analysis');
|
|
780
|
+
for (const af of opts.analystFindings) {
|
|
781
|
+
const r = af.result;
|
|
782
|
+
if (af.taskType === 'threatAnalysis') {
|
|
783
|
+
const level = String(r.threatLevel ?? 'unknown').toUpperCase();
|
|
784
|
+
const levelColor = level === 'CRITICAL' || level === 'HIGH' ? colors.red : level === 'MEDIUM' ? colors.yellow : colors.dim;
|
|
785
|
+
console.log(` ${levelColor}${colors.bold}${level}${RESET()} ${r.attackVector ?? ''}`);
|
|
786
|
+
if (r.description)
|
|
787
|
+
console.log(` ${colors.dim}${r.description}${RESET()}`);
|
|
788
|
+
if (Array.isArray(r.mitigations) && r.mitigations.length > 0) {
|
|
789
|
+
for (const m of r.mitigations) {
|
|
790
|
+
console.log(` ${colors.cyan}Fix:${RESET()} ${m}`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else if (af.taskType === 'credentialContextClassification') {
|
|
795
|
+
const cls = String(r.classification ?? 'unknown');
|
|
796
|
+
const clsColor = cls === 'real' ? colors.red : cls === 'test' || cls === 'example' ? colors.green : colors.yellow;
|
|
797
|
+
console.log(` Credential: ${clsColor}${colors.bold}${cls}${RESET()}`);
|
|
798
|
+
if (r.reasoning)
|
|
799
|
+
console.log(` ${colors.dim}${r.reasoning}${RESET()}`);
|
|
800
|
+
}
|
|
801
|
+
else if (af.taskType === 'intelReport') {
|
|
802
|
+
if (r.summary)
|
|
803
|
+
console.log(` ${colors.cyan}Summary:${RESET()} ${r.summary}`);
|
|
804
|
+
if (Array.isArray(r.keyFindings) && r.keyFindings.length > 0) {
|
|
805
|
+
for (const kf of r.keyFindings) {
|
|
806
|
+
console.log(` ${colors.dim}${kf}${RESET()}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (r.riskAssessment)
|
|
810
|
+
console.log(` ${colors.cyan}Risk:${RESET()} ${r.riskAssessment}`);
|
|
811
|
+
if (Array.isArray(r.recommendations) && r.recommendations.length > 0) {
|
|
812
|
+
for (const rec of r.recommendations) {
|
|
813
|
+
console.log(` ${colors.dim}${rec}${RESET()}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else if (af.taskType === 'governanceReasoning') {
|
|
818
|
+
if (Array.isArray(r.gaps) && r.gaps.length > 0) {
|
|
819
|
+
console.log(` ${colors.yellow}Governance gaps:${RESET()}`);
|
|
820
|
+
for (const gap of r.gaps)
|
|
821
|
+
console.log(` ${colors.dim}- ${gap}${RESET()}`);
|
|
822
|
+
}
|
|
823
|
+
if (Array.isArray(r.recommendations) && r.recommendations.length > 0) {
|
|
824
|
+
for (const rec of r.recommendations) {
|
|
825
|
+
console.log(` ${colors.cyan}Fix:${RESET()} ${rec}`);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
else if (af.taskType === 'checkExplanation') {
|
|
830
|
+
if (r.explanation)
|
|
831
|
+
console.log(` ${r.explanation}`);
|
|
832
|
+
if (r.impact)
|
|
833
|
+
console.log(` ${colors.yellow}Impact:${RESET()} ${r.impact}`);
|
|
834
|
+
if (r.recommendation)
|
|
835
|
+
console.log(` ${colors.cyan}Fix:${RESET()} ${r.recommendation}`);
|
|
836
|
+
}
|
|
837
|
+
else if (af.taskType === 'falsePositiveDetection') {
|
|
838
|
+
const fp = Boolean(r.isFalsePositive);
|
|
839
|
+
console.log(` ${fp ? colors.green : colors.yellow}${fp ? 'Likely false positive' : 'Likely real finding'}${RESET()}`);
|
|
840
|
+
if (r.reasoning)
|
|
841
|
+
console.log(` ${colors.dim}${r.reasoning}${RESET()}`);
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
// Generic display
|
|
845
|
+
if (r.description)
|
|
846
|
+
console.log(` ${r.description}`);
|
|
847
|
+
}
|
|
848
|
+
console.log(` ${colors.dim}Confidence: ${Math.round(af.confidence * 100)}% | ${af.modelVersion} (${af.durationMs}ms)${RESET()}`);
|
|
849
|
+
console.log();
|
|
850
|
+
}
|
|
851
|
+
}
|
|
776
852
|
// ── Next steps ──────────────────────────────────────────────────────
|
|
777
853
|
const hasGovIssues = failed.some(f => f.category === 'governance' || f.category === 'Governance' || f.checkId?.startsWith('AST-GOV') || f.checkId?.startsWith('AST-PROMPT'));
|
|
778
854
|
const hasCredIssues = failed.some(f => f.checkId?.startsWith('CRED-') || f.name?.toLowerCase().includes('credential') || f.name?.toLowerCase().includes('api key') || f.name?.toLowerCase().includes('hardcoded'));
|
|
@@ -6319,7 +6395,8 @@ function looksLikeRawUrl(target) {
|
|
|
6319
6395
|
*/
|
|
6320
6396
|
function parseGitHubTarget(target) {
|
|
6321
6397
|
// Full URL: https://github.com/org/repo[.git][/tree/...]
|
|
6322
|
-
|
|
6398
|
+
// Allow dots in repo names (e.g. next.js, vue.js) but strip trailing .git
|
|
6399
|
+
const urlMatch = target.match(/^https?:\/\/(www\.)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/|$)/);
|
|
6323
6400
|
if (urlMatch) {
|
|
6324
6401
|
return {
|
|
6325
6402
|
org: urlMatch[2],
|
|
@@ -6603,6 +6680,77 @@ const TEST_FILE_PATTERNS = [
|
|
|
6603
6680
|
/[^/]+\.spec\.\w+$/,
|
|
6604
6681
|
/\bfixtures?\//i,
|
|
6605
6682
|
];
|
|
6683
|
+
/**
|
|
6684
|
+
* Documentation and generated file patterns.
|
|
6685
|
+
* These files describe security concepts (credentials, prompts, governance)
|
|
6686
|
+
* but are not attack surfaces themselves. Governance/credential/prompt
|
|
6687
|
+
* findings on these are almost always false positives in cloned repos.
|
|
6688
|
+
*/
|
|
6689
|
+
const DOCS_AND_GENERATED_PATTERNS = [
|
|
6690
|
+
/\.md$/i, // Markdown documentation
|
|
6691
|
+
/\.rst$/i, // reStructuredText docs
|
|
6692
|
+
/\bdocs?\//i, // docs/ directory
|
|
6693
|
+
/\bdocumentation\//i, // documentation/ directory
|
|
6694
|
+
/\bexamples?\//i, // examples/ directory
|
|
6695
|
+
/\bsamples?\//i, // samples/ directory
|
|
6696
|
+
/openapi[^/]*\.json$/i, // OpenAPI spec files
|
|
6697
|
+
/openapi[^/]*\.ya?ml$/i, // OpenAPI spec YAML files
|
|
6698
|
+
/swagger[^/]*\.json$/i, // Swagger spec files
|
|
6699
|
+
/swagger[^/]*\.ya?ml$/i, // Swagger spec YAML files
|
|
6700
|
+
/\bapi\/openapi-spec\//i, // API spec directories
|
|
6701
|
+
/\bapi\/discovery\//i, // API discovery docs
|
|
6702
|
+
/\bvendor\//i, // vendored dependencies
|
|
6703
|
+
/\bthird.?party\//i, // third-party code
|
|
6704
|
+
/\bCHANGELOG/i, // changelog files
|
|
6705
|
+
/\bHISTORY/i, // history files
|
|
6706
|
+
/\bLICENSE/i, // license files
|
|
6707
|
+
/\bCONTRIBUTING/i, // contributing guides
|
|
6708
|
+
];
|
|
6709
|
+
function isDocsOrGenerated(filePath) {
|
|
6710
|
+
return DOCS_AND_GENERATED_PATTERNS.some(p => p.test(filePath));
|
|
6711
|
+
}
|
|
6712
|
+
/**
|
|
6713
|
+
* Build scripts, CI/CD pipelines, and infrastructure files.
|
|
6714
|
+
* These are not runtime attack surfaces — findings here are lower risk.
|
|
6715
|
+
*/
|
|
6716
|
+
const BUILD_CI_PATTERNS = [
|
|
6717
|
+
/\bbuild\//i, // build/ directory
|
|
6718
|
+
/\bdist\//i, // dist/ directory
|
|
6719
|
+
/\b\.github\//, // GitHub Actions
|
|
6720
|
+
/\b\.circleci\//, // CircleCI
|
|
6721
|
+
/\bazure-pipelines/i, // Azure Pipelines
|
|
6722
|
+
/\bjenkins/i, // Jenkins
|
|
6723
|
+
/\b\.gitlab-ci/, // GitLab CI
|
|
6724
|
+
/\bMakefile$/i, // Makefiles
|
|
6725
|
+
/\bGruntfile/i, // Grunt
|
|
6726
|
+
/\bgulpfile/i, // Gulp
|
|
6727
|
+
/\bwebpack\.\w+\.js$/i, // Webpack configs
|
|
6728
|
+
/\brollup\.\w+\.js$/i, // Rollup configs
|
|
6729
|
+
/\bscripts\//i, // scripts/ directory
|
|
6730
|
+
/\btools?\//i, // tools/ directory
|
|
6731
|
+
/\binfra\//i, // infra/ directory
|
|
6732
|
+
];
|
|
6733
|
+
function isBuildOrCiFile(filePath) {
|
|
6734
|
+
return BUILD_CI_PATTERNS.some(p => p.test(filePath));
|
|
6735
|
+
}
|
|
6736
|
+
/**
|
|
6737
|
+
* Check IDs for security-sensitive pattern matches that produce false
|
|
6738
|
+
* positives on documentation and generated files. These checks look for
|
|
6739
|
+
* credential patterns, prompt injection, governance gaps, and skill
|
|
6740
|
+
* definitions — all of which appear naturally in docs that DESCRIBE
|
|
6741
|
+
* these concepts without being vulnerable.
|
|
6742
|
+
*/
|
|
6743
|
+
const DOCS_FALSE_POSITIVE_PREFIXES = [
|
|
6744
|
+
'AST-GOV', // governance checks
|
|
6745
|
+
'AST-GOVERN', // governance checks
|
|
6746
|
+
'AST-PROMPT', // prompt security checks
|
|
6747
|
+
'AST-HEARTBEAT', // heartbeat/liveness checks
|
|
6748
|
+
'AST-CRED', // credential pattern checks
|
|
6749
|
+
'AST-INJECT', // injection pattern checks
|
|
6750
|
+
'AST-EXFIL', // exfiltration pattern checks
|
|
6751
|
+
'SKILL-', // skill definition checks
|
|
6752
|
+
'SUPPLY-', // supply chain checks
|
|
6753
|
+
];
|
|
6606
6754
|
function isTestFile(filePath) {
|
|
6607
6755
|
return TEST_FILE_PATTERNS.some(p => p.test(filePath));
|
|
6608
6756
|
}
|
|
@@ -6612,8 +6760,10 @@ function isAiToolingFile(filePath) {
|
|
|
6612
6760
|
/**
|
|
6613
6761
|
* Filter out local-dev-only findings that are meaningless for downloaded
|
|
6614
6762
|
* packages (e.g. "Missing .gitignore" on an npm tarball). Also filters
|
|
6615
|
-
* governance findings on AI tooling files
|
|
6616
|
-
*
|
|
6763
|
+
* governance findings on AI tooling files, removes false-positive
|
|
6764
|
+
* pattern matches on documentation/generated files, and demotes test
|
|
6765
|
+
* file findings. Mutates `result.findings` in place and recalculates
|
|
6766
|
+
* the score.
|
|
6617
6767
|
*/
|
|
6618
6768
|
function filterLocalOnlyFindings(result, scanner) {
|
|
6619
6769
|
result.findings = result.findings.filter(f => {
|
|
@@ -6626,14 +6776,29 @@ function filterLocalOnlyFindings(result, scanner) {
|
|
|
6626
6776
|
// files are false positives — they describe security practices, not vulnerabilities.
|
|
6627
6777
|
if (f.file && isAiToolingFile(f.file))
|
|
6628
6778
|
return false;
|
|
6779
|
+
// Exclude governance/credential/prompt/skill pattern-match findings on
|
|
6780
|
+
// documentation, generated specs, and vendored files. These files
|
|
6781
|
+
// naturally describe security concepts without being vulnerable.
|
|
6782
|
+
// Structural checks (unicode steganography, TOCTOU, deserialization)
|
|
6783
|
+
// are kept — those detect actual content issues regardless of file type.
|
|
6784
|
+
if (f.file && isDocsOrGenerated(f.file)) {
|
|
6785
|
+
const checkId = f.checkId || '';
|
|
6786
|
+
if (DOCS_FALSE_POSITIVE_PREFIXES.some(p => checkId.startsWith(p))) {
|
|
6787
|
+
return false;
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6629
6790
|
return true;
|
|
6630
6791
|
});
|
|
6631
|
-
// Demote test file findings to low severity
|
|
6632
|
-
// lower risk
|
|
6792
|
+
// Demote test file and build script findings to low severity.
|
|
6793
|
+
// Test code patterns are lower risk (pickle.load in a test file is not
|
|
6794
|
+
// an attack surface). Build scripts, CI configs, and vendored code are
|
|
6795
|
+
// not runtime attack surfaces for the end user.
|
|
6633
6796
|
for (const f of result.findings) {
|
|
6634
|
-
if (f.file &&
|
|
6635
|
-
f.
|
|
6636
|
-
|
|
6797
|
+
if (f.file && (f.severity === 'critical' || f.severity === 'high')) {
|
|
6798
|
+
if (isTestFile(f.file) || isBuildOrCiFile(f.file)) {
|
|
6799
|
+
f.originalSeverity = f.severity;
|
|
6800
|
+
f.severity = 'low';
|
|
6801
|
+
}
|
|
6637
6802
|
}
|
|
6638
6803
|
}
|
|
6639
6804
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
@@ -6666,10 +6831,16 @@ function printCheckNextSteps(target, context) {
|
|
|
6666
6831
|
}
|
|
6667
6832
|
if (context?.hasFindings) {
|
|
6668
6833
|
console.log(` ${colors.cyan}Full project audit:${RESET()} ${getFullScanHint()}`);
|
|
6834
|
+
if (!context?.usedAnalm) {
|
|
6835
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6836
|
+
}
|
|
6669
6837
|
}
|
|
6670
6838
|
else if (context?.isCleanScan && isLocal) {
|
|
6671
6839
|
console.log(` ${colors.cyan}Governance scan:${RESET()} ${CLI_PREFIX} scan-soul ${target}`);
|
|
6672
6840
|
console.log(` ${colors.cyan}Red-team test:${RESET()} ${CLI_PREFIX} attack --local`);
|
|
6841
|
+
if (!context?.usedAnalm) {
|
|
6842
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6843
|
+
}
|
|
6673
6844
|
}
|
|
6674
6845
|
else if (context?.isCleanScan) {
|
|
6675
6846
|
if (!context?.usedAnalm) {
|
|
@@ -6785,6 +6956,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6785
6956
|
const scanner = new index_1.HardeningScanner();
|
|
6786
6957
|
const result = await scanner.scan({ targetDir: repoDir, autoFix: false });
|
|
6787
6958
|
// Run NanoMind semantic analysis and re-filter
|
|
6959
|
+
let analystFindings;
|
|
6788
6960
|
try {
|
|
6789
6961
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6790
6962
|
const nmResult = await orchestrateNanoMind(repoDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6792,6 +6964,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6792
6964
|
const projectType = result.projectType || 'library';
|
|
6793
6965
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6794
6966
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
6967
|
+
analystFindings = nmResult.analystFindings;
|
|
6795
6968
|
}
|
|
6796
6969
|
catch {
|
|
6797
6970
|
// NanoMind unavailable — use base scan results
|
|
@@ -6804,7 +6977,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6804
6977
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6805
6978
|
const low = failed.filter(f => f.severity === 'low');
|
|
6806
6979
|
if (options.json) {
|
|
6807
|
-
|
|
6980
|
+
const jsonOut = {
|
|
6808
6981
|
name: displayName,
|
|
6809
6982
|
type: 'github-repo',
|
|
6810
6983
|
source: 'local-scan',
|
|
@@ -6812,7 +6985,10 @@ async function checkGitHubRepo(target, options) {
|
|
|
6812
6985
|
score: result.score,
|
|
6813
6986
|
maxScore: result.maxScore,
|
|
6814
6987
|
findings: result.findings,
|
|
6815
|
-
}
|
|
6988
|
+
};
|
|
6989
|
+
if (analystFindings?.length)
|
|
6990
|
+
jsonOut.analystFindings = analystFindings;
|
|
6991
|
+
writeJsonStdout(jsonOut);
|
|
6816
6992
|
return;
|
|
6817
6993
|
}
|
|
6818
6994
|
// Await registry data (started in parallel with clone)
|
|
@@ -6825,6 +7001,8 @@ async function checkGitHubRepo(target, options) {
|
|
|
6825
7001
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6826
7002
|
registry: registryData,
|
|
6827
7003
|
verbose: !!options.verbose,
|
|
7004
|
+
usedAnalm: !!options.analm,
|
|
7005
|
+
analystFindings,
|
|
6828
7006
|
});
|
|
6829
7007
|
// Community contribution
|
|
6830
7008
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -6948,6 +7126,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6948
7126
|
const scanner = new index_1.HardeningScanner();
|
|
6949
7127
|
const result = await scanner.scan({ targetDir: extractDir, autoFix: false });
|
|
6950
7128
|
// Run NanoMind semantic analysis and re-filter
|
|
7129
|
+
let analystFindings;
|
|
6951
7130
|
try {
|
|
6952
7131
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6953
7132
|
const nmResult = await orchestrateNanoMind(extractDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6955,6 +7134,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6955
7134
|
const projectType = result.projectType || 'library';
|
|
6956
7135
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6957
7136
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7137
|
+
analystFindings = nmResult.analystFindings;
|
|
6958
7138
|
}
|
|
6959
7139
|
catch {
|
|
6960
7140
|
// NanoMind unavailable -- use base scan results
|
|
@@ -6967,7 +7147,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6967
7147
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6968
7148
|
const low = failed.filter(f => f.severity === 'low');
|
|
6969
7149
|
if (options.json) {
|
|
6970
|
-
|
|
7150
|
+
const jsonOut = {
|
|
6971
7151
|
name,
|
|
6972
7152
|
type: 'pypi-package',
|
|
6973
7153
|
source: 'local-scan',
|
|
@@ -6976,7 +7156,10 @@ async function checkPyPiPackage(target, options) {
|
|
|
6976
7156
|
score: result.score,
|
|
6977
7157
|
maxScore: result.maxScore,
|
|
6978
7158
|
findings: result.findings,
|
|
6979
|
-
}
|
|
7159
|
+
};
|
|
7160
|
+
if (analystFindings?.length)
|
|
7161
|
+
jsonOut.analystFindings = analystFindings;
|
|
7162
|
+
writeJsonStdout(jsonOut);
|
|
6980
7163
|
return;
|
|
6981
7164
|
}
|
|
6982
7165
|
// Display results using unified display
|
|
@@ -6990,6 +7173,8 @@ async function checkPyPiPackage(target, options) {
|
|
|
6990
7173
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6991
7174
|
registry: registryData,
|
|
6992
7175
|
verbose: !!options.verbose,
|
|
7176
|
+
usedAnalm: !!options.analm,
|
|
7177
|
+
analystFindings,
|
|
6993
7178
|
});
|
|
6994
7179
|
if (critical.length > 0 || high.length > 0)
|
|
6995
7180
|
process.exit(1);
|
|
@@ -7103,6 +7288,7 @@ async function checkRawUrl(url, options) {
|
|
|
7103
7288
|
// Run full HMA scan + NanoMind
|
|
7104
7289
|
const scanner = new index_1.HardeningScanner();
|
|
7105
7290
|
const result = await scanner.scan({ targetDir: scanDir, autoFix: false });
|
|
7291
|
+
let analystFindings;
|
|
7106
7292
|
try {
|
|
7107
7293
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7108
7294
|
const nmResult = await orchestrateNanoMind(scanDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7110,6 +7296,7 @@ async function checkRawUrl(url, options) {
|
|
|
7110
7296
|
const projectType = result.projectType || 'library';
|
|
7111
7297
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7112
7298
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7299
|
+
analystFindings = nmResult.analystFindings;
|
|
7113
7300
|
}
|
|
7114
7301
|
catch {
|
|
7115
7302
|
// NanoMind unavailable — use base scan results
|
|
@@ -7122,7 +7309,7 @@ async function checkRawUrl(url, options) {
|
|
|
7122
7309
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
7123
7310
|
const low = failed.filter(f => f.severity === 'low');
|
|
7124
7311
|
if (options.json) {
|
|
7125
|
-
|
|
7312
|
+
const jsonOut = {
|
|
7126
7313
|
name: displayName,
|
|
7127
7314
|
url,
|
|
7128
7315
|
type: 'raw-url',
|
|
@@ -7131,7 +7318,10 @@ async function checkRawUrl(url, options) {
|
|
|
7131
7318
|
score: result.score,
|
|
7132
7319
|
maxScore: result.maxScore,
|
|
7133
7320
|
findings: result.findings,
|
|
7134
|
-
}
|
|
7321
|
+
};
|
|
7322
|
+
if (analystFindings?.length)
|
|
7323
|
+
jsonOut.analystFindings = analystFindings;
|
|
7324
|
+
writeJsonStdout(jsonOut);
|
|
7135
7325
|
return;
|
|
7136
7326
|
}
|
|
7137
7327
|
// Display results using unified display
|
|
@@ -7141,6 +7331,8 @@ async function checkRawUrl(url, options) {
|
|
|
7141
7331
|
projectType: result.projectType,
|
|
7142
7332
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7143
7333
|
verbose: !!options.verbose,
|
|
7334
|
+
usedAnalm: !!options.analm,
|
|
7335
|
+
analystFindings,
|
|
7144
7336
|
});
|
|
7145
7337
|
// Community contribution (auto-share if opted in, no first-time prompt for URLs)
|
|
7146
7338
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -7185,7 +7377,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7185
7377
|
writeJsonStdout({ ...registryData, source: 'registry' });
|
|
7186
7378
|
return;
|
|
7187
7379
|
}
|
|
7188
|
-
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose });
|
|
7380
|
+
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose, usedAnalm: !!options.analm });
|
|
7189
7381
|
return;
|
|
7190
7382
|
}
|
|
7191
7383
|
if (!options.json && !globalCiMode) {
|
|
@@ -7241,6 +7433,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7241
7433
|
const scanner = new index_1.HardeningScanner();
|
|
7242
7434
|
const result = await scanner.scan({ targetDir: packageDir, autoFix: false });
|
|
7243
7435
|
// Run NanoMind semantic analysis and re-filter (matches secure command pipeline)
|
|
7436
|
+
let analystFindings;
|
|
7244
7437
|
try {
|
|
7245
7438
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7246
7439
|
const nmResult = await orchestrateNanoMind(packageDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7248,6 +7441,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7248
7441
|
const projectType = result.projectType || 'library';
|
|
7249
7442
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7250
7443
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7444
|
+
analystFindings = nmResult.analystFindings;
|
|
7251
7445
|
}
|
|
7252
7446
|
catch {
|
|
7253
7447
|
// NanoMind unavailable — use base scan results
|
|
@@ -7258,7 +7452,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7258
7452
|
const critical = failed.filter(f => f.severity === 'critical');
|
|
7259
7453
|
const high = failed.filter(f => f.severity === 'high');
|
|
7260
7454
|
if (options.json) {
|
|
7261
|
-
|
|
7455
|
+
const jsonOut = {
|
|
7262
7456
|
name,
|
|
7263
7457
|
type: 'npm-package',
|
|
7264
7458
|
source: 'local-scan',
|
|
@@ -7266,7 +7460,10 @@ async function checkNpmPackage(name, options) {
|
|
|
7266
7460
|
score: result.score,
|
|
7267
7461
|
maxScore: result.maxScore,
|
|
7268
7462
|
findings: result.findings,
|
|
7269
|
-
}
|
|
7463
|
+
};
|
|
7464
|
+
if (analystFindings?.length)
|
|
7465
|
+
jsonOut.analystFindings = analystFindings;
|
|
7466
|
+
writeJsonStdout(jsonOut);
|
|
7270
7467
|
return;
|
|
7271
7468
|
}
|
|
7272
7469
|
// Await registry data (started in parallel with download)
|
|
@@ -7278,6 +7475,8 @@ async function checkNpmPackage(name, options) {
|
|
|
7278
7475
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7279
7476
|
registry: registryData,
|
|
7280
7477
|
verbose: !!options.verbose,
|
|
7478
|
+
usedAnalm: !!options.analm,
|
|
7479
|
+
analystFindings,
|
|
7281
7480
|
});
|
|
7282
7481
|
// Community contribution (after 3 scans, interactive only)
|
|
7283
7482
|
if (process.stdin.isTTY && !globalCiMode) {
|