hackmyagent 0.17.5 → 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 +244 -22
- 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.d.ts.map +1 -1
- package/dist/nanomind-core/orchestrate.js +48 -14
- package/dist/nanomind-core/orchestrate.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -246,6 +246,8 @@ Examples:
|
|
|
246
246
|
findings: issues,
|
|
247
247
|
},
|
|
248
248
|
verbose: !!options.verbose,
|
|
249
|
+
usedAnalm: !!options.analm,
|
|
250
|
+
analystFindings: nmResult.analystFindings,
|
|
249
251
|
});
|
|
250
252
|
const risk = critical.length > 0 ? 'critical' : high.length > 0 ? 'high' : issues.length > 0 ? 'medium' : 'low';
|
|
251
253
|
if (risk === 'critical' || risk === 'high')
|
|
@@ -452,7 +454,7 @@ function shortenPath(filePath) {
|
|
|
452
454
|
return parts.slice(-2).join('/');
|
|
453
455
|
}
|
|
454
456
|
function displayUnifiedCheck(opts) {
|
|
455
|
-
const { name, sourceLabel, projectType, localScan, registry, verbose, version, nanomindScan } = opts;
|
|
457
|
+
const { name, sourceLabel, projectType, localScan, registry, verbose, version, nanomindScan, usedAnalm } = opts;
|
|
456
458
|
// ── Visual helpers ──────────────────────────────────────────────────
|
|
457
459
|
const METER_WIDTH = 20;
|
|
458
460
|
const divider = (label) => {
|
|
@@ -772,6 +774,81 @@ function displayUnifiedCheck(opts) {
|
|
|
772
774
|
console.log(` ${colors.dim}${legend}${RESET()}`);
|
|
773
775
|
}
|
|
774
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
|
+
}
|
|
775
852
|
// ── Next steps ──────────────────────────────────────────────────────
|
|
776
853
|
const hasGovIssues = failed.some(f => f.category === 'governance' || f.category === 'Governance' || f.checkId?.startsWith('AST-GOV') || f.checkId?.startsWith('AST-PROMPT'));
|
|
777
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'));
|
|
@@ -787,6 +864,7 @@ function displayUnifiedCheck(opts) {
|
|
|
787
864
|
hasCredentialFindings: hasCredIssues,
|
|
788
865
|
hasCodeVulns,
|
|
789
866
|
isCleanScan: totalFindings === 0 && (!!localScan || !!nanomindScan),
|
|
867
|
+
usedAnalm,
|
|
790
868
|
});
|
|
791
869
|
}
|
|
792
870
|
function groupFindingsBySeverity(findings) {
|
|
@@ -2778,9 +2856,26 @@ Examples:
|
|
|
2778
2856
|
if (r.reasoning)
|
|
2779
2857
|
console.log(` ${r.reasoning}`);
|
|
2780
2858
|
}
|
|
2859
|
+
else if (af.taskType === 'intelReport') {
|
|
2860
|
+
if (r.summary)
|
|
2861
|
+
console.log(` ${colors.cyan}Summary:${RESET()} ${r.summary}`);
|
|
2862
|
+
if (Array.isArray(r.keyFindings) && r.keyFindings.length > 0) {
|
|
2863
|
+
for (const kf of r.keyFindings) {
|
|
2864
|
+
console.log(` ${kf}`);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
if (r.riskAssessment)
|
|
2868
|
+
console.log(` ${colors.cyan}Risk:${RESET()} ${r.riskAssessment}`);
|
|
2869
|
+
if (Array.isArray(r.recommendations) && r.recommendations.length > 0) {
|
|
2870
|
+
for (const rec of r.recommendations) {
|
|
2871
|
+
console.log(` ${colors.dim}${rec}${RESET()}`);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2781
2875
|
else {
|
|
2782
2876
|
// Generic display for other task types
|
|
2783
|
-
|
|
2877
|
+
if (r.description)
|
|
2878
|
+
console.log(` ${r.description}`);
|
|
2784
2879
|
}
|
|
2785
2880
|
console.log(` ${colors.dim}Confidence: ${Math.round(af.confidence * 100)}% | ${af.modelVersion} (${af.durationMs}ms)${RESET()}`);
|
|
2786
2881
|
console.log();
|
|
@@ -6300,7 +6395,8 @@ function looksLikeRawUrl(target) {
|
|
|
6300
6395
|
*/
|
|
6301
6396
|
function parseGitHubTarget(target) {
|
|
6302
6397
|
// Full URL: https://github.com/org/repo[.git][/tree/...]
|
|
6303
|
-
|
|
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)?(?:\/|$)/);
|
|
6304
6400
|
if (urlMatch) {
|
|
6305
6401
|
return {
|
|
6306
6402
|
org: urlMatch[2],
|
|
@@ -6584,6 +6680,77 @@ const TEST_FILE_PATTERNS = [
|
|
|
6584
6680
|
/[^/]+\.spec\.\w+$/,
|
|
6585
6681
|
/\bfixtures?\//i,
|
|
6586
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
|
+
];
|
|
6587
6754
|
function isTestFile(filePath) {
|
|
6588
6755
|
return TEST_FILE_PATTERNS.some(p => p.test(filePath));
|
|
6589
6756
|
}
|
|
@@ -6593,8 +6760,10 @@ function isAiToolingFile(filePath) {
|
|
|
6593
6760
|
/**
|
|
6594
6761
|
* Filter out local-dev-only findings that are meaningless for downloaded
|
|
6595
6762
|
* packages (e.g. "Missing .gitignore" on an npm tarball). Also filters
|
|
6596
|
-
* governance findings on AI tooling files
|
|
6597
|
-
*
|
|
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.
|
|
6598
6767
|
*/
|
|
6599
6768
|
function filterLocalOnlyFindings(result, scanner) {
|
|
6600
6769
|
result.findings = result.findings.filter(f => {
|
|
@@ -6607,14 +6776,29 @@ function filterLocalOnlyFindings(result, scanner) {
|
|
|
6607
6776
|
// files are false positives — they describe security practices, not vulnerabilities.
|
|
6608
6777
|
if (f.file && isAiToolingFile(f.file))
|
|
6609
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
|
+
}
|
|
6610
6790
|
return true;
|
|
6611
6791
|
});
|
|
6612
|
-
// Demote test file findings to low severity
|
|
6613
|
-
// 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.
|
|
6614
6796
|
for (const f of result.findings) {
|
|
6615
|
-
if (f.file &&
|
|
6616
|
-
f.
|
|
6617
|
-
|
|
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
|
+
}
|
|
6618
6802
|
}
|
|
6619
6803
|
}
|
|
6620
6804
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
@@ -6647,16 +6831,26 @@ function printCheckNextSteps(target, context) {
|
|
|
6647
6831
|
}
|
|
6648
6832
|
if (context?.hasFindings) {
|
|
6649
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
|
+
}
|
|
6650
6837
|
}
|
|
6651
6838
|
else if (context?.isCleanScan && isLocal) {
|
|
6652
6839
|
console.log(` ${colors.cyan}Governance scan:${RESET()} ${CLI_PREFIX} scan-soul ${target}`);
|
|
6653
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
|
+
}
|
|
6654
6844
|
}
|
|
6655
6845
|
else if (context?.isCleanScan) {
|
|
6656
|
-
|
|
6846
|
+
if (!context?.usedAnalm) {
|
|
6847
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6848
|
+
}
|
|
6657
6849
|
}
|
|
6658
6850
|
else {
|
|
6659
|
-
|
|
6851
|
+
if (!context?.usedAnalm) {
|
|
6852
|
+
console.log(` ${colors.cyan}AI analysis:${RESET()} ${CLI_PREFIX} check ${target} --analm`);
|
|
6853
|
+
}
|
|
6660
6854
|
}
|
|
6661
6855
|
console.log();
|
|
6662
6856
|
}
|
|
@@ -6736,7 +6930,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6736
6930
|
writeJsonStdout({ ...registryData, source: 'registry' });
|
|
6737
6931
|
return;
|
|
6738
6932
|
}
|
|
6739
|
-
displayUnifiedCheck({ name: displayName, sourceLabel: 'GitHub', registry: registryData, verbose: !!options.verbose });
|
|
6933
|
+
displayUnifiedCheck({ name: displayName, sourceLabel: 'GitHub', registry: registryData, verbose: !!options.verbose, usedAnalm: !!options.analm });
|
|
6740
6934
|
return;
|
|
6741
6935
|
}
|
|
6742
6936
|
if (!options.json && !globalCiMode) {
|
|
@@ -6762,6 +6956,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6762
6956
|
const scanner = new index_1.HardeningScanner();
|
|
6763
6957
|
const result = await scanner.scan({ targetDir: repoDir, autoFix: false });
|
|
6764
6958
|
// Run NanoMind semantic analysis and re-filter
|
|
6959
|
+
let analystFindings;
|
|
6765
6960
|
try {
|
|
6766
6961
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6767
6962
|
const nmResult = await orchestrateNanoMind(repoDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6769,6 +6964,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6769
6964
|
const projectType = result.projectType || 'library';
|
|
6770
6965
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6771
6966
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
6967
|
+
analystFindings = nmResult.analystFindings;
|
|
6772
6968
|
}
|
|
6773
6969
|
catch {
|
|
6774
6970
|
// NanoMind unavailable — use base scan results
|
|
@@ -6781,7 +6977,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6781
6977
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6782
6978
|
const low = failed.filter(f => f.severity === 'low');
|
|
6783
6979
|
if (options.json) {
|
|
6784
|
-
|
|
6980
|
+
const jsonOut = {
|
|
6785
6981
|
name: displayName,
|
|
6786
6982
|
type: 'github-repo',
|
|
6787
6983
|
source: 'local-scan',
|
|
@@ -6789,7 +6985,10 @@ async function checkGitHubRepo(target, options) {
|
|
|
6789
6985
|
score: result.score,
|
|
6790
6986
|
maxScore: result.maxScore,
|
|
6791
6987
|
findings: result.findings,
|
|
6792
|
-
}
|
|
6988
|
+
};
|
|
6989
|
+
if (analystFindings?.length)
|
|
6990
|
+
jsonOut.analystFindings = analystFindings;
|
|
6991
|
+
writeJsonStdout(jsonOut);
|
|
6793
6992
|
return;
|
|
6794
6993
|
}
|
|
6795
6994
|
// Await registry data (started in parallel with clone)
|
|
@@ -6802,6 +7001,8 @@ async function checkGitHubRepo(target, options) {
|
|
|
6802
7001
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6803
7002
|
registry: registryData,
|
|
6804
7003
|
verbose: !!options.verbose,
|
|
7004
|
+
usedAnalm: !!options.analm,
|
|
7005
|
+
analystFindings,
|
|
6805
7006
|
});
|
|
6806
7007
|
// Community contribution
|
|
6807
7008
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -6925,6 +7126,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6925
7126
|
const scanner = new index_1.HardeningScanner();
|
|
6926
7127
|
const result = await scanner.scan({ targetDir: extractDir, autoFix: false });
|
|
6927
7128
|
// Run NanoMind semantic analysis and re-filter
|
|
7129
|
+
let analystFindings;
|
|
6928
7130
|
try {
|
|
6929
7131
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
6930
7132
|
const nmResult = await orchestrateNanoMind(extractDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -6932,6 +7134,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6932
7134
|
const projectType = result.projectType || 'library';
|
|
6933
7135
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
6934
7136
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7137
|
+
analystFindings = nmResult.analystFindings;
|
|
6935
7138
|
}
|
|
6936
7139
|
catch {
|
|
6937
7140
|
// NanoMind unavailable -- use base scan results
|
|
@@ -6944,7 +7147,7 @@ async function checkPyPiPackage(target, options) {
|
|
|
6944
7147
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
6945
7148
|
const low = failed.filter(f => f.severity === 'low');
|
|
6946
7149
|
if (options.json) {
|
|
6947
|
-
|
|
7150
|
+
const jsonOut = {
|
|
6948
7151
|
name,
|
|
6949
7152
|
type: 'pypi-package',
|
|
6950
7153
|
source: 'local-scan',
|
|
@@ -6953,7 +7156,10 @@ async function checkPyPiPackage(target, options) {
|
|
|
6953
7156
|
score: result.score,
|
|
6954
7157
|
maxScore: result.maxScore,
|
|
6955
7158
|
findings: result.findings,
|
|
6956
|
-
}
|
|
7159
|
+
};
|
|
7160
|
+
if (analystFindings?.length)
|
|
7161
|
+
jsonOut.analystFindings = analystFindings;
|
|
7162
|
+
writeJsonStdout(jsonOut);
|
|
6957
7163
|
return;
|
|
6958
7164
|
}
|
|
6959
7165
|
// Display results using unified display
|
|
@@ -6967,6 +7173,8 @@ async function checkPyPiPackage(target, options) {
|
|
|
6967
7173
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
6968
7174
|
registry: registryData,
|
|
6969
7175
|
verbose: !!options.verbose,
|
|
7176
|
+
usedAnalm: !!options.analm,
|
|
7177
|
+
analystFindings,
|
|
6970
7178
|
});
|
|
6971
7179
|
if (critical.length > 0 || high.length > 0)
|
|
6972
7180
|
process.exit(1);
|
|
@@ -7080,6 +7288,7 @@ async function checkRawUrl(url, options) {
|
|
|
7080
7288
|
// Run full HMA scan + NanoMind
|
|
7081
7289
|
const scanner = new index_1.HardeningScanner();
|
|
7082
7290
|
const result = await scanner.scan({ targetDir: scanDir, autoFix: false });
|
|
7291
|
+
let analystFindings;
|
|
7083
7292
|
try {
|
|
7084
7293
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7085
7294
|
const nmResult = await orchestrateNanoMind(scanDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7087,6 +7296,7 @@ async function checkRawUrl(url, options) {
|
|
|
7087
7296
|
const projectType = result.projectType || 'library';
|
|
7088
7297
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7089
7298
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7299
|
+
analystFindings = nmResult.analystFindings;
|
|
7090
7300
|
}
|
|
7091
7301
|
catch {
|
|
7092
7302
|
// NanoMind unavailable — use base scan results
|
|
@@ -7099,7 +7309,7 @@ async function checkRawUrl(url, options) {
|
|
|
7099
7309
|
const medium = failed.filter(f => f.severity === 'medium');
|
|
7100
7310
|
const low = failed.filter(f => f.severity === 'low');
|
|
7101
7311
|
if (options.json) {
|
|
7102
|
-
|
|
7312
|
+
const jsonOut = {
|
|
7103
7313
|
name: displayName,
|
|
7104
7314
|
url,
|
|
7105
7315
|
type: 'raw-url',
|
|
@@ -7108,7 +7318,10 @@ async function checkRawUrl(url, options) {
|
|
|
7108
7318
|
score: result.score,
|
|
7109
7319
|
maxScore: result.maxScore,
|
|
7110
7320
|
findings: result.findings,
|
|
7111
|
-
}
|
|
7321
|
+
};
|
|
7322
|
+
if (analystFindings?.length)
|
|
7323
|
+
jsonOut.analystFindings = analystFindings;
|
|
7324
|
+
writeJsonStdout(jsonOut);
|
|
7112
7325
|
return;
|
|
7113
7326
|
}
|
|
7114
7327
|
// Display results using unified display
|
|
@@ -7118,6 +7331,8 @@ async function checkRawUrl(url, options) {
|
|
|
7118
7331
|
projectType: result.projectType,
|
|
7119
7332
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7120
7333
|
verbose: !!options.verbose,
|
|
7334
|
+
usedAnalm: !!options.analm,
|
|
7335
|
+
analystFindings,
|
|
7121
7336
|
});
|
|
7122
7337
|
// Community contribution (auto-share if opted in, no first-time prompt for URLs)
|
|
7123
7338
|
if (process.stdin.isTTY && !globalCiMode) {
|
|
@@ -7162,7 +7377,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7162
7377
|
writeJsonStdout({ ...registryData, source: 'registry' });
|
|
7163
7378
|
return;
|
|
7164
7379
|
}
|
|
7165
|
-
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose });
|
|
7380
|
+
displayUnifiedCheck({ name, registry: registryData, verbose: !!options.verbose, usedAnalm: !!options.analm });
|
|
7166
7381
|
return;
|
|
7167
7382
|
}
|
|
7168
7383
|
if (!options.json && !globalCiMode) {
|
|
@@ -7218,6 +7433,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7218
7433
|
const scanner = new index_1.HardeningScanner();
|
|
7219
7434
|
const result = await scanner.scan({ targetDir: packageDir, autoFix: false });
|
|
7220
7435
|
// Run NanoMind semantic analysis and re-filter (matches secure command pipeline)
|
|
7436
|
+
let analystFindings;
|
|
7221
7437
|
try {
|
|
7222
7438
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
7223
7439
|
const nmResult = await orchestrateNanoMind(packageDir, result.findings, { silent: true, analm: options.analm });
|
|
@@ -7225,6 +7441,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7225
7441
|
const projectType = result.projectType || 'library';
|
|
7226
7442
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
7227
7443
|
result.score = scanner.calculateScore(result.findings.filter((f) => !f.passed && !f.fixed)).score;
|
|
7444
|
+
analystFindings = nmResult.analystFindings;
|
|
7228
7445
|
}
|
|
7229
7446
|
catch {
|
|
7230
7447
|
// NanoMind unavailable — use base scan results
|
|
@@ -7235,7 +7452,7 @@ async function checkNpmPackage(name, options) {
|
|
|
7235
7452
|
const critical = failed.filter(f => f.severity === 'critical');
|
|
7236
7453
|
const high = failed.filter(f => f.severity === 'high');
|
|
7237
7454
|
if (options.json) {
|
|
7238
|
-
|
|
7455
|
+
const jsonOut = {
|
|
7239
7456
|
name,
|
|
7240
7457
|
type: 'npm-package',
|
|
7241
7458
|
source: 'local-scan',
|
|
@@ -7243,7 +7460,10 @@ async function checkNpmPackage(name, options) {
|
|
|
7243
7460
|
score: result.score,
|
|
7244
7461
|
maxScore: result.maxScore,
|
|
7245
7462
|
findings: result.findings,
|
|
7246
|
-
}
|
|
7463
|
+
};
|
|
7464
|
+
if (analystFindings?.length)
|
|
7465
|
+
jsonOut.analystFindings = analystFindings;
|
|
7466
|
+
writeJsonStdout(jsonOut);
|
|
7247
7467
|
return;
|
|
7248
7468
|
}
|
|
7249
7469
|
// Await registry data (started in parallel with download)
|
|
@@ -7255,6 +7475,8 @@ async function checkNpmPackage(name, options) {
|
|
|
7255
7475
|
localScan: { score: result.score, maxScore: result.maxScore, findings: result.findings },
|
|
7256
7476
|
registry: registryData,
|
|
7257
7477
|
verbose: !!options.verbose,
|
|
7478
|
+
usedAnalm: !!options.analm,
|
|
7479
|
+
analystFindings,
|
|
7258
7480
|
});
|
|
7259
7481
|
// Community contribution (after 3 scans, interactive only)
|
|
7260
7482
|
if (process.stdin.isTTY && !globalCiMode) {
|