mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-29 16:03:34 +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', () => {
|
it('does not use clean ClawHub scanner summaries for blocked scans', () => {
|
||||||
expect(normalizeClawHubScan({
|
expect(normalizeClawHubScan({
|
||||||
status: 'malicious',
|
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', () => {
|
it('does not use clean ClawHub scanner summaries for unknown scans', () => {
|
||||||
expect(normalizeClawHubScan({
|
expect(normalizeClawHubScan({
|
||||||
status: 'unknown',
|
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', () => {
|
it('normalizes SkillHub list items as fallback candidates with Chinese summary', () => {
|
||||||
const result = normalizeSkillHubList(SKILLHUB_TOP_SKILLS_RESPONSE)
|
const result = normalizeSkillHubList(SKILLHUB_TOP_SKILLS_RESPONSE)
|
||||||
|
|
||||||
@ -294,7 +345,25 @@ describe('skill market source normalization', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(detail.trustState).toBe('unknown')
|
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.status).toBe('blocked')
|
||||||
expect(detail.installEligibility.reason).toMatch(/security report is missing or inconclusive/i)
|
expect(detail.installEligibility.reason).toMatch(/security report is missing or inconclusive/i)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -57,32 +57,39 @@ export function normalizeClawHubScan(payload: ClawHubScanResponse): {
|
|||||||
packageSha256?: string
|
packageSha256?: string
|
||||||
} {
|
} {
|
||||||
const scannerEntries = Object.values(payload.scanners ?? {})
|
const scannerEntries = Object.values(payload.scanners ?? {})
|
||||||
|
const blockedStatuses = ['malicious', 'blocked']
|
||||||
|
const warningStatuses = ['suspicious', 'warning']
|
||||||
const scannerSummary = scannerEntries.find((entry) => entry.summary)?.summary
|
const scannerSummary = scannerEntries.find((entry) => entry.summary)?.summary
|
||||||
const scannerSummaryForStatuses = (statuses: string[]) =>
|
const scannerSummaryForStatuses = (statuses: string[]) =>
|
||||||
scannerEntries.find((entry) => entry.summary && entry.status && statuses.includes(entry.status))?.summary
|
scannerEntries.find((entry) => entry.summary && entry.status && statuses.includes(entry.status))?.summary
|
||||||
const scannerSummaryExcludingStatuses = (statuses: string[]) =>
|
const hasScannerStatus = (statuses: string[]) =>
|
||||||
scannerEntries.find((entry) => entry.summary && (!entry.status || !statuses.includes(entry.status)))?.summary
|
scannerEntries.some((entry) => entry.status && statuses.includes(entry.status))
|
||||||
|
|
||||||
if (payload.status === 'clean' && !payload.hasWarnings) {
|
if (payload.status === 'malicious' || payload.status === 'blocked' || hasScannerStatus(blockedStatuses)) {
|
||||||
return { trustState: 'clean', trustSummary: scannerSummary, packageSha256: payload.sha256 }
|
|
||||||
}
|
|
||||||
if (payload.status === 'malicious' || payload.status === 'blocked') {
|
|
||||||
return {
|
return {
|
||||||
trustState: 'blocked',
|
trustState: 'blocked',
|
||||||
trustSummary: scannerSummaryForStatuses(['malicious', 'blocked']),
|
trustSummary: scannerSummaryForStatuses(blockedStatuses),
|
||||||
packageSha256: payload.sha256,
|
packageSha256: payload.sha256,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.status === 'suspicious' || payload.hasWarnings) {
|
if (
|
||||||
|
payload.status === 'suspicious'
|
||||||
|
|| payload.status === 'warning'
|
||||||
|
|| payload.hasWarnings
|
||||||
|
|| hasScannerStatus(warningStatuses)
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
trustState: 'warning',
|
trustState: 'warning',
|
||||||
trustSummary: scannerSummaryForStatuses(['suspicious', 'warning']),
|
trustSummary: scannerSummaryForStatuses(warningStatuses),
|
||||||
packageSha256: payload.sha256,
|
packageSha256: payload.sha256,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (payload.status === 'clean') {
|
||||||
|
return { trustState: 'clean', trustSummary: scannerSummary, packageSha256: payload.sha256 }
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
trustState: 'unknown',
|
trustState: 'unknown',
|
||||||
trustSummary: scannerSummaryExcludingStatuses(['benign', 'clean']),
|
trustSummary: undefined,
|
||||||
packageSha256: payload.sha256,
|
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_BLOCKED_REPORT_STATUSES = new Set(['malicious', 'blocked'])
|
||||||
const SKILLHUB_WARNING_REPORT_STATUSES = new Set(['warning', 'suspicious'])
|
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 }) {
|
function requiresApiKey(labels?: { requires_api_key?: string }) {
|
||||||
return labels?.requires_api_key === 'true'
|
return labels?.requires_api_key === 'true'
|
||||||
@ -96,8 +95,6 @@ function trustFromReports(reports?: Record<string, { status?: string; statusText
|
|||||||
const values = Object.values(reports ?? {})
|
const values = Object.values(reports ?? {})
|
||||||
const summaryForStatuses = (statuses: Set<string>) =>
|
const summaryForStatuses = (statuses: Set<string>) =>
|
||||||
values.find((report) => report.status && statuses.has(report.status) && report.statusText)?.statusText
|
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))) {
|
if (values.some((report) => report.status && SKILLHUB_BLOCKED_REPORT_STATUSES.has(report.status))) {
|
||||||
return {
|
return {
|
||||||
@ -114,7 +111,7 @@ function trustFromReports(reports?: Record<string, { status?: string; statusText
|
|||||||
if (values.length > 0 && values.every((report) => report.status === 'benign')) {
|
if (values.length > 0 && values.every((report) => report.status === 'benign')) {
|
||||||
return { trustState: 'benign', trustSummary: values.find((report) => report.statusText)?.statusText }
|
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'] {
|
function installEligibilityFromTrustState(trustState: SkillMarketTrustState): SkillMarketDetail['installEligibility'] {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user