mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
fix: prioritize risky clawhub scanner results
Tested: bun test src/server/__tests__/skill-market.test.ts Tested: git diff --check Confidence: high Scope-risk: narrow
This commit is contained in:
parent
b6bdc00877
commit
7a35116ee5
@ -70,6 +70,19 @@ describe('skill market source normalization', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('prioritizes malicious ClawHub scanner results over clean top-level status', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'clean',
|
||||
scanners: {
|
||||
metadata: { status: 'clean', summary: 'No dangerous patterns detected.' },
|
||||
staticAnalysis: { status: 'malicious', summary: 'Credential exfiltration detected.' },
|
||||
},
|
||||
})).toMatchObject({
|
||||
trustState: 'blocked',
|
||||
trustSummary: 'Credential exfiltration detected.',
|
||||
})
|
||||
})
|
||||
|
||||
it('does not use clean ClawHub scanner summaries for blocked scans', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'malicious',
|
||||
@ -96,6 +109,31 @@ describe('skill market source normalization', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('prioritizes warning ClawHub scanner results over clean top-level status', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'clean',
|
||||
scanners: {
|
||||
metadata: { status: 'clean', summary: 'No dangerous patterns detected.' },
|
||||
staticAnalysis: { status: 'warning', summary: 'Reads shell profile files.' },
|
||||
},
|
||||
})).toMatchObject({
|
||||
trustState: 'warning',
|
||||
trustSummary: 'Reads shell profile files.',
|
||||
})
|
||||
})
|
||||
|
||||
it('maps ClawHub top-level warning status to warning trust state', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'warning',
|
||||
scanners: {
|
||||
staticAnalysis: { status: 'warning', summary: 'Reads shell profile files.' },
|
||||
},
|
||||
})).toMatchObject({
|
||||
trustState: 'warning',
|
||||
trustSummary: 'Reads shell profile files.',
|
||||
})
|
||||
})
|
||||
|
||||
it('does not use clean ClawHub scanner summaries for unknown scans', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'unknown',
|
||||
@ -109,6 +147,19 @@ describe('skill market source normalization', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('does not use unscored ClawHub scanner summaries for unknown scans', () => {
|
||||
expect(normalizeClawHubScan({
|
||||
status: 'unknown',
|
||||
scanners: {
|
||||
metadata: { summary: 'No dangerous patterns detected.' },
|
||||
},
|
||||
})).toEqual({
|
||||
trustState: 'unknown',
|
||||
trustSummary: undefined,
|
||||
packageSha256: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('normalizes SkillHub list items as fallback candidates with Chinese summary', () => {
|
||||
const result = normalizeSkillHubList(SKILLHUB_TOP_SKILLS_RESPONSE)
|
||||
|
||||
@ -294,7 +345,25 @@ describe('skill market source normalization', () => {
|
||||
})
|
||||
|
||||
expect(detail.trustState).toBe('unknown')
|
||||
expect(detail.trustSummary).toBe('Scanner still reviewing.')
|
||||
expect(detail.trustSummary).toBeUndefined()
|
||||
expect(detail.installEligibility.status).toBe('blocked')
|
||||
expect(detail.installEligibility.reason).toMatch(/security report is missing or inconclusive/i)
|
||||
})
|
||||
|
||||
it('does not use unscored SkillHub report summaries for unknown details', () => {
|
||||
const detail = normalizeSkillHubDetail({
|
||||
securityReports: {
|
||||
community: { status: 'benign', statusText: 'safe' },
|
||||
staticAnalysis: { statusText: 'No issues detected.' },
|
||||
},
|
||||
skill: {
|
||||
slug: 'unscored-report-skill',
|
||||
displayName: 'Unscored Report Skill',
|
||||
},
|
||||
})
|
||||
|
||||
expect(detail.trustState).toBe('unknown')
|
||||
expect(detail.trustSummary).toBeUndefined()
|
||||
expect(detail.installEligibility.status).toBe('blocked')
|
||||
expect(detail.installEligibility.reason).toMatch(/security report is missing or inconclusive/i)
|
||||
})
|
||||
|
||||
@ -57,32 +57,39 @@ export function normalizeClawHubScan(payload: ClawHubScanResponse): {
|
||||
packageSha256?: string
|
||||
} {
|
||||
const scannerEntries = Object.values(payload.scanners ?? {})
|
||||
const blockedStatuses = ['malicious', 'blocked']
|
||||
const warningStatuses = ['suspicious', 'warning']
|
||||
const scannerSummary = scannerEntries.find((entry) => entry.summary)?.summary
|
||||
const scannerSummaryForStatuses = (statuses: string[]) =>
|
||||
scannerEntries.find((entry) => entry.summary && entry.status && statuses.includes(entry.status))?.summary
|
||||
const scannerSummaryExcludingStatuses = (statuses: string[]) =>
|
||||
scannerEntries.find((entry) => entry.summary && (!entry.status || !statuses.includes(entry.status)))?.summary
|
||||
const hasScannerStatus = (statuses: string[]) =>
|
||||
scannerEntries.some((entry) => entry.status && statuses.includes(entry.status))
|
||||
|
||||
if (payload.status === 'clean' && !payload.hasWarnings) {
|
||||
return { trustState: 'clean', trustSummary: scannerSummary, packageSha256: payload.sha256 }
|
||||
}
|
||||
if (payload.status === 'malicious' || payload.status === 'blocked') {
|
||||
if (payload.status === 'malicious' || payload.status === 'blocked' || hasScannerStatus(blockedStatuses)) {
|
||||
return {
|
||||
trustState: 'blocked',
|
||||
trustSummary: scannerSummaryForStatuses(['malicious', 'blocked']),
|
||||
trustSummary: scannerSummaryForStatuses(blockedStatuses),
|
||||
packageSha256: payload.sha256,
|
||||
}
|
||||
}
|
||||
if (payload.status === 'suspicious' || payload.hasWarnings) {
|
||||
if (
|
||||
payload.status === 'suspicious'
|
||||
|| payload.status === 'warning'
|
||||
|| payload.hasWarnings
|
||||
|| hasScannerStatus(warningStatuses)
|
||||
) {
|
||||
return {
|
||||
trustState: 'warning',
|
||||
trustSummary: scannerSummaryForStatuses(['suspicious', 'warning']),
|
||||
trustSummary: scannerSummaryForStatuses(warningStatuses),
|
||||
packageSha256: payload.sha256,
|
||||
}
|
||||
}
|
||||
if (payload.status === 'clean') {
|
||||
return { trustState: 'clean', trustSummary: scannerSummary, packageSha256: payload.sha256 }
|
||||
}
|
||||
return {
|
||||
trustState: 'unknown',
|
||||
trustSummary: scannerSummaryExcludingStatuses(['benign', 'clean']),
|
||||
trustSummary: undefined,
|
||||
packageSha256: payload.sha256,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,6 @@ const SKILLHUB_DETAIL_INSTALLABLE_TRUST_STATES = new Set<SkillMarketTrustState>(
|
||||
])
|
||||
const SKILLHUB_BLOCKED_REPORT_STATUSES = new Set(['malicious', 'blocked'])
|
||||
const SKILLHUB_WARNING_REPORT_STATUSES = new Set(['warning', 'suspicious'])
|
||||
const SKILLHUB_BENIGN_REPORT_STATUSES = new Set(['benign', 'clean'])
|
||||
|
||||
function requiresApiKey(labels?: { requires_api_key?: string }) {
|
||||
return labels?.requires_api_key === 'true'
|
||||
@ -96,8 +95,6 @@ function trustFromReports(reports?: Record<string, { status?: string; statusText
|
||||
const values = Object.values(reports ?? {})
|
||||
const summaryForStatuses = (statuses: Set<string>) =>
|
||||
values.find((report) => report.status && statuses.has(report.status) && report.statusText)?.statusText
|
||||
const summaryExcludingStatuses = (statuses: Set<string>) =>
|
||||
values.find((report) => (!report.status || !statuses.has(report.status)) && report.statusText)?.statusText
|
||||
|
||||
if (values.some((report) => report.status && SKILLHUB_BLOCKED_REPORT_STATUSES.has(report.status))) {
|
||||
return {
|
||||
@ -114,7 +111,7 @@ function trustFromReports(reports?: Record<string, { status?: string; statusText
|
||||
if (values.length > 0 && values.every((report) => report.status === 'benign')) {
|
||||
return { trustState: 'benign', trustSummary: values.find((report) => report.statusText)?.statusText }
|
||||
}
|
||||
return { trustState: 'unknown', trustSummary: summaryExcludingStatuses(SKILLHUB_BENIGN_REPORT_STATUSES) }
|
||||
return { trustState: 'unknown' }
|
||||
}
|
||||
|
||||
function installEligibilityFromTrustState(trustState: SkillMarketTrustState): SkillMarketDetail['installEligibility'] {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user