hackmyagent 0.17.6 → 0.17.8
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 +221 -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/inference/analm-infer-llamacpp.py +113 -0
- package/dist/nanomind-core/inference/security-analyst.d.ts +1 -1
- package/dist/nanomind-core/inference/security-analyst.d.ts.map +1 -1
- package/dist/nanomind-core/inference/security-analyst.js +108 -21
- package/dist/nanomind-core/inference/security-analyst.js.map +1 -1
- package/dist/nanomind-core/orchestrate.js +19 -5
- package/dist/nanomind-core/orchestrate.js.map +1 -1
- package/package.json +2 -2
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,17 +6680,95 @@ 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
|
+
/\.tmLanguage\.json$/i, // TextMate grammars
|
|
6709
|
+
/\.schema\.json$/i, // JSON schema definitions
|
|
6710
|
+
/\.nls\.json$/i, // localization/NLS files
|
|
6711
|
+
/\bcglicenses/i, // CG license files
|
|
6712
|
+
/\blicenses?\//i, // license directories
|
|
6713
|
+
];
|
|
6714
|
+
/**
|
|
6715
|
+
* Check IDs for security-sensitive pattern matches that produce false
|
|
6716
|
+
* positives on documentation and generated files. These checks look for
|
|
6717
|
+
* credential patterns, prompt injection, governance gaps, and skill
|
|
6718
|
+
* definitions — all of which appear naturally in docs that DESCRIBE
|
|
6719
|
+
* these concepts without being vulnerable.
|
|
6720
|
+
*/
|
|
6721
|
+
const DOCS_FALSE_POSITIVE_PREFIXES = [
|
|
6722
|
+
'AST-GOV', // governance checks
|
|
6723
|
+
'AST-GOVERN', // governance checks
|
|
6724
|
+
'AST-PROMPT', // prompt security checks
|
|
6725
|
+
'AST-HEARTBEAT', // heartbeat/liveness checks
|
|
6726
|
+
'AST-CRED', // credential pattern checks
|
|
6727
|
+
'AST-INJECT', // injection pattern checks
|
|
6728
|
+
'AST-EXFIL', // exfiltration pattern checks
|
|
6729
|
+
'SKILL-', // skill definition checks
|
|
6730
|
+
'SUPPLY-', // supply chain checks
|
|
6731
|
+
];
|
|
6732
|
+
/**
|
|
6733
|
+
* Build scripts, CI/CD pipelines, and infrastructure files.
|
|
6734
|
+
* These are not runtime attack surfaces — findings here are lower risk.
|
|
6735
|
+
*/
|
|
6736
|
+
const BUILD_CI_PATTERNS = [
|
|
6737
|
+
/\bbuild\//i, // build/ directory
|
|
6738
|
+
/\bdist\//i, // dist/ directory
|
|
6739
|
+
/\b\.github\//, // GitHub Actions
|
|
6740
|
+
/\b\.circleci\//, // CircleCI
|
|
6741
|
+
/\bazure-pipelines/i, // Azure Pipelines
|
|
6742
|
+
/\bjenkins/i, // Jenkins
|
|
6743
|
+
/\b\.gitlab-ci/, // GitLab CI
|
|
6744
|
+
/\bMakefile$/i, // Makefiles
|
|
6745
|
+
/\bGruntfile/i, // Grunt
|
|
6746
|
+
/\bgulpfile/i, // Gulp
|
|
6747
|
+
/\bwebpack\.\w+\.js$/i, // Webpack configs
|
|
6748
|
+
/\brollup\.\w+\.js$/i, // Rollup configs
|
|
6749
|
+
/\bscripts\//i, // scripts/ directory
|
|
6750
|
+
/\btools?\//i, // tools/ directory
|
|
6751
|
+
/\binfra\//i, // infra/ directory
|
|
6752
|
+
];
|
|
6606
6753
|
function isTestFile(filePath) {
|
|
6607
6754
|
return TEST_FILE_PATTERNS.some(p => p.test(filePath));
|
|
6608
6755
|
}
|
|
6756
|
+
function isDocsOrGenerated(filePath) {
|
|
6757
|
+
return DOCS_AND_GENERATED_PATTERNS.some(p => p.test(filePath));
|
|
6758
|
+
}
|
|
6759
|
+
function isBuildOrCiFile(filePath) {
|
|
6760
|
+
return BUILD_CI_PATTERNS.some(p => p.test(filePath));
|
|
6761
|
+
}
|
|
6609
6762
|
function isAiToolingFile(filePath) {
|
|
6610
6763
|
return AI_TOOLING_PATH_PATTERNS.some(p => p.test(filePath));
|
|
6611
6764
|
}
|
|
6612
6765
|
/**
|
|
6613
6766
|
* Filter out local-dev-only findings that are meaningless for downloaded
|
|
6614
6767
|
* packages (e.g. "Missing .gitignore" on an npm tarball). Also filters
|
|
6615
|
-
* governance findings on AI tooling files
|
|
6616
|
-
*
|
|
6768
|
+
* governance findings on AI tooling files, removes false-positive
|
|
6769
|
+
* pattern matches on documentation/generated files, and demotes test
|
|
6770
|
+
* and build file findings. Mutates `result.findings` in place and
|
|
6771
|
+
* recalculates the score.
|
|
6617
6772
|
*/
|
|
6618
6773
|
function filterLocalOnlyFindings(result, scanner) {
|
|
6619
6774
|
result.findings = result.findings.filter(f => {
|
|
@@ -6626,14 +6781,29 @@ function filterLocalOnlyFindings(result, scanner) {
|
|
|
6626
6781
|
// files are false positives — they describe security practices, not vulnerabilities.
|
|
6627
6782
|
if (f.file && isAiToolingFile(f.file))
|
|
6628
6783
|
return false;
|
|
6784
|
+
// Exclude governance/credential/prompt/skill pattern-match findings on
|
|
6785
|
+
// documentation, generated specs, and vendored files. These files
|
|
6786
|
+
// naturally describe security concepts without being vulnerable.
|
|
6787
|
+
// Structural checks (unicode steganography, TOCTOU, deserialization)
|
|
6788
|
+
// are kept — those detect actual content issues regardless of file type.
|
|
6789
|
+
if (f.file && isDocsOrGenerated(f.file)) {
|
|
6790
|
+
const checkId = f.checkId || '';
|
|
6791
|
+
if (DOCS_FALSE_POSITIVE_PREFIXES.some(p => checkId.startsWith(p))) {
|
|
6792
|
+
return false;
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6629
6795
|
return true;
|
|
6630
6796
|
});
|
|
6631
|
-
// Demote test file findings to low severity
|
|
6632
|
-
// lower risk
|
|
6797
|
+
// Demote test file and build script findings to low severity.
|
|
6798
|
+
// Test code patterns are lower risk (pickle.load in a test file is not
|
|
6799
|
+
// an attack surface). Build scripts, CI configs, and vendored code are
|
|
6800
|
+
// not runtime attack surfaces for the end user.
|
|
6633
6801
|
for (const f of result.findings) {
|
|
6634
|
-
if (f.file &&
|
|
6635
|
-
f.
|
|
6636
|
-
|
|
6802
|
+
if (f.file && (f.severity === 'critical' || f.severity === 'high')) {
|
|
6803
|
+
if (isTestFile(f.file) || isBuildOrCiFile(f.file)) {
|
|
6804
|
+
f.originalSeverity = f.severity;
|
|
6805
|
+
f.severity = 'low';
|
|
6806
|
+
}
|
|
6637
6807
|
}
|
|
6638
6808
|
}
|
|
6639
6809
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
@@ -6666,10 +6836,16 @@ function printCheckNextSteps(target, context) {
|
|
|
6666
6836
|
}
|
|
6667
6837
|
if (context?.hasFindings) {
|
|
6668
6838
|
console.log(` ${colors.cyan}Full project audit:${RESET()} ${getFullScanHint()}`);
|
|
6839
|
+
if (!context?.usedAnalm) {
|
|
6840
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6841
|
+
}
|
|
6669
6842
|
}
|
|
6670
6843
|
else if (context?.isCleanScan && isLocal) {
|
|
6671
6844
|
console.log(` ${colors.cyan}Governance scan:${RESET()} ${CLI_PREFIX} scan-soul ${target}`);
|
|
6672
6845
|
console.log(` ${colors.cyan}Red-team test:${RESET()} ${CLI_PREFIX} attack --local`);
|
|
6846
|
+
if (!context?.usedAnalm) {
|
|
6847
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6848
|
+
}
|
|
6673
6849
|
}
|
|
6674
6850
|
else if (context?.isCleanScan) {
|
|
6675
6851
|
if (!context?.usedAnalm) {
|
|
@@ -6785,6 +6961,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6785
6961
|
const scanner = new index_1.HardeningScanner();
|
|
6786
6962
|
const result = await scanner.scan({ targetDir: repoDir, autoFix: false });
|
|
6787
6963
|
// Run NanoMind semantic analysis and re-filter
|
|
6964
|
+
let analystFindings;
|
|
6788
6965
|
try {
|
|
6789
6966
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6790
6967
|
const nmResult = await orchestrateNanoMind(repoDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6792,6 +6969,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6792
6969
|
const projectType = result.projectType || 'library';
|
|
6793
6970
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6794
6971
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
6972
|
+
analystFindings = nmResult.analystFindings;
|
|
6795
6973
|
}
|
|
6796
6974
|
catch {
|
|
6797
6975
|
// NanoMind unavailable — use base scan results
|
|
@@ -6804,7 +6982,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6804
6982
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6805
6983
|
const low = failed.filter(f => f.severity === 'low');
|
|
6806
6984
|
if (options.json) {
|
|
6807
|
-
|
|
6985
|
+
const jsonOut = {
|
|
6808
6986
|
name: displayName,
|
|
6809
6987
|
type: 'github-repo',
|
|
6810
6988
|
source: 'local-scan',
|
|
@@ -6812,7 +6990,10 @@ async function checkGitHubRepo(target, options) {
|
|
|
6812
6990
|
score: result.score,
|
|
6813
6991
|
maxScore: result.maxScore,
|
|
6814
6992
|
findings: result.findings,
|
|
6815
|
-
}
|
|
6993
|
+
};
|
|
6994
|
+
if (analystFindings?.length)
|
|
6995
|
+
jsonOut.analystFindings = analystFindings;
|
|
6996
|
+
writeJsonStdout(jsonOut);
|
|
6816
6997
|
return;
|
|
6817
6998
|
}
|
|
6818
6999
|
// Await registry data (started in parallel with clone)
|
|
@@ -6825,6 +7006,8 @@ async function checkGitHubRepo(target, options) {
|
|
|
6825
7006
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6826
7007
|
registry: registryData,
|
|
6827
7008
|
verbose: !!options.verbose,
|
|
7009
|
+
usedAnalm: !!options.analm,
|
|
7010
|
+
analystFindings,
|
|
6828
7011
|
});
|
|
6829
7012
|
// Community contribution
|
|
6830
7013
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -6948,6 +7131,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6948
7131
|
const scanner = new index_1.HardeningScanner();
|
|
6949
7132
|
const result = await scanner.scan({ targetDir: extractDir, autoFix: false });
|
|
6950
7133
|
// Run NanoMind semantic analysis and re-filter
|
|
7134
|
+
let analystFindings;
|
|
6951
7135
|
try {
|
|
6952
7136
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6953
7137
|
const nmResult = await orchestrateNanoMind(extractDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6955,6 +7139,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6955
7139
|
const projectType = result.projectType || 'library';
|
|
6956
7140
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6957
7141
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7142
|
+
analystFindings = nmResult.analystFindings;
|
|
6958
7143
|
}
|
|
6959
7144
|
catch {
|
|
6960
7145
|
// NanoMind unavailable -- use base scan results
|
|
@@ -6967,7 +7152,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6967
7152
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6968
7153
|
const low = failed.filter(f => f.severity === 'low');
|
|
6969
7154
|
if (options.json) {
|
|
6970
|
-
|
|
7155
|
+
const jsonOut = {
|
|
6971
7156
|
name,
|
|
6972
7157
|
type: 'pypi-package',
|
|
6973
7158
|
source: 'local-scan',
|
|
@@ -6976,7 +7161,10 @@ async function checkPyPiPackage(target, options) {
|
|
|
6976
7161
|
score: result.score,
|
|
6977
7162
|
maxScore: result.maxScore,
|
|
6978
7163
|
findings: result.findings,
|
|
6979
|
-
}
|
|
7164
|
+
};
|
|
7165
|
+
if (analystFindings?.length)
|
|
7166
|
+
jsonOut.analystFindings = analystFindings;
|
|
7167
|
+
writeJsonStdout(jsonOut);
|
|
6980
7168
|
return;
|
|
6981
7169
|
}
|
|
6982
7170
|
// Display results using unified display
|
|
@@ -6990,6 +7178,8 @@ async function checkPyPiPackage(target, options) {
|
|
|
6990
7178
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6991
7179
|
registry: registryData,
|
|
6992
7180
|
verbose: !!options.verbose,
|
|
7181
|
+
usedAnalm: !!options.analm,
|
|
7182
|
+
analystFindings,
|
|
6993
7183
|
});
|
|
6994
7184
|
if (critical.length > 0 || high.length > 0)
|
|
6995
7185
|
process.exit(1);
|
|
@@ -7103,6 +7293,7 @@ async function checkRawUrl(url, options) {
|
|
|
7103
7293
|
// Run full HMA scan + NanoMind
|
|
7104
7294
|
const scanner = new index_1.HardeningScanner();
|
|
7105
7295
|
const result = await scanner.scan({ targetDir: scanDir, autoFix: false });
|
|
7296
|
+
let analystFindings;
|
|
7106
7297
|
try {
|
|
7107
7298
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7108
7299
|
const nmResult = await orchestrateNanoMind(scanDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7110,6 +7301,7 @@ async function checkRawUrl(url, options) {
|
|
|
7110
7301
|
const projectType = result.projectType || 'library';
|
|
7111
7302
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7112
7303
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7304
|
+
analystFindings = nmResult.analystFindings;
|
|
7113
7305
|
}
|
|
7114
7306
|
catch {
|
|
7115
7307
|
// NanoMind unavailable — use base scan results
|
|
@@ -7122,7 +7314,7 @@ async function checkRawUrl(url, options) {
|
|
|
7122
7314
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
7123
7315
|
const low = failed.filter(f => f.severity === 'low');
|
|
7124
7316
|
if (options.json) {
|
|
7125
|
-
|
|
7317
|
+
const jsonOut = {
|
|
7126
7318
|
name: displayName,
|
|
7127
7319
|
url,
|
|
7128
7320
|
type: 'raw-url',
|
|
@@ -7131,7 +7323,10 @@ async function checkRawUrl(url, options) {
|
|
|
7131
7323
|
score: result.score,
|
|
7132
7324
|
maxScore: result.maxScore,
|
|
7133
7325
|
findings: result.findings,
|
|
7134
|
-
}
|
|
7326
|
+
};
|
|
7327
|
+
if (analystFindings?.length)
|
|
7328
|
+
jsonOut.analystFindings = analystFindings;
|
|
7329
|
+
writeJsonStdout(jsonOut);
|
|
7135
7330
|
return;
|
|
7136
7331
|
}
|
|
7137
7332
|
// Display results using unified display
|
|
@@ -7141,6 +7336,8 @@ async function checkRawUrl(url, options) {
|
|
|
7141
7336
|
projectType: result.projectType,
|
|
7142
7337
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7143
7338
|
verbose: !!options.verbose,
|
|
7339
|
+
usedAnalm: !!options.analm,
|
|
7340
|
+
analystFindings,
|
|
7144
7341
|
});
|
|
7145
7342
|
// Community contribution (auto-share if opted in, no first-time prompt for URLs)
|
|
7146
7343
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -7185,7 +7382,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7185
7382
|
writeJsonStdout({ ...registryData, source: 'registry' });
|
|
7186
7383
|
return;
|
|
7187
7384
|
}
|
|
7188
|
-
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose });
|
|
7385
|
+
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose, usedAnalm: !!options.analm });
|
|
7189
7386
|
return;
|
|
7190
7387
|
}
|
|
7191
7388
|
if (!options.json && !globalCiMode) {
|
|
@@ -7241,6 +7438,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7241
7438
|
const scanner = new index_1.HardeningScanner();
|
|
7242
7439
|
const result = await scanner.scan({ targetDir: packageDir, autoFix: false });
|
|
7243
7440
|
// Run NanoMind semantic analysis and re-filter (matches secure command pipeline)
|
|
7441
|
+
let analystFindings;
|
|
7244
7442
|
try {
|
|
7245
7443
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7246
7444
|
const nmResult = await orchestrateNanoMind(packageDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7248,6 +7446,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7248
7446
|
const projectType = result.projectType || 'library';
|
|
7249
7447
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7250
7448
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7449
|
+
analystFindings = nmResult.analystFindings;
|
|
7251
7450
|
}
|
|
7252
7451
|
catch {
|
|
7253
7452
|
// NanoMind unavailable — use base scan results
|
|
@@ -7258,7 +7457,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7258
7457
|
const critical = failed.filter(f => f.severity === 'critical');
|
|
7259
7458
|
const high = failed.filter(f => f.severity === 'high');
|
|
7260
7459
|
if (options.json) {
|
|
7261
|
-
|
|
7460
|
+
const jsonOut = {
|
|
7262
7461
|
name,
|
|
7263
7462
|
type: 'npm-package',
|
|
7264
7463
|
source: 'local-scan',
|
|
@@ -7266,7 +7465,10 @@ async function checkNpmPackage(name, options) {
|
|
|
7266
7465
|
score: result.score,
|
|
7267
7466
|
maxScore: result.maxScore,
|
|
7268
7467
|
findings: result.findings,
|
|
7269
|
-
}
|
|
7468
|
+
};
|
|
7469
|
+
if (analystFindings?.length)
|
|
7470
|
+
jsonOut.analystFindings = analystFindings;
|
|
7471
|
+
writeJsonStdout(jsonOut);
|
|
7270
7472
|
return;
|
|
7271
7473
|
}
|
|
7272
7474
|
// Await registry data (started in parallel with download)
|
|
@@ -7278,6 +7480,8 @@ async function checkNpmPackage(name, options) {
|
|
|
7278
7480
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7279
7481
|
registry: registryData,
|
|
7280
7482
|
verbose: !!options.verbose,
|
|
7483
|
+
usedAnalm: !!options.analm,
|
|
7484
|
+
analystFindings,
|
|
7281
7485
|
});
|
|
7282
7486
|
// Community contribution (after 3 scans, interactive only)
|
|
7283
7487
|
if (process.stdin.isTTY && !globalCiMode) {
|