Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions src/application/evaluation-readiness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
SignalFrequency,
} from '../domain/contracts.js'
import type { PackageNode, RiskSignal } from '../domain/entities.js'
import { isSecurityRelatedDeprecation } from '../domain/security-deprecation.js'

// Export-readiness exclusions are single-reason buckets. This precedence keeps
// counts deterministic even when one row fails multiple readiness checks.
Expand All @@ -30,14 +31,6 @@ const EXPORT_EXCLUSION_ORDER = [
'unavailable_field_dependency',
] as const

const SECURITY_DEPRECATION_PATTERNS = [
/security vulnerability/i,
/security issue/i,
/critical vulnerability/i,
/cve-/i,
/cve /i,
] as const

interface EvaluationDatasetSummary {
signal_frequency: SignalFrequency[]
metadata_coverage: MetadataCoverageSummary
Expand Down Expand Up @@ -319,7 +312,7 @@ export function buildEvaluationDatasetSummary(scanRecords: ScanReviewRecord[]):
* @returns `true` when the message matches the evaluation security patterns.
*/
export function isSecurityRelatedDeprecationMessage(message: string): boolean {
return SECURITY_DEPRECATION_PATTERNS.some((pattern) => pattern.test(message))
return isSecurityRelatedDeprecation(message)
}

function determineExportBlockingReasons({
Expand Down
8 changes: 8 additions & 0 deletions src/domain/security-deprecation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Prefer structured CVE identifiers over bare "cve" so incidental references
// do not count as security-related deprecation evidence without an actual id.
const SECURITY_RELATED_DEPRECATION_PATTERN =
/\b(?:security|vulnerab(?:ility|ilities)|cve-\d{4}-\d+)\b/i

export function isSecurityRelatedDeprecation(message: string): boolean {
return SECURITY_RELATED_DEPRECATION_PATTERN.test(message)
}
6 changes: 2 additions & 4 deletions src/domain/value-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { basename } from 'node:path'
import type { BaselineIdentity, PackageSpec, ResolvedPackage, ScanMode } from './contracts.js'
import type { Recommendation, RiskLevel, RiskSignal } from './entities.js'
import { InvalidUsageError } from './errors.js'
import { isSecurityRelatedDeprecation } from './security-deprecation.js'

export const DEFAULT_MAX_DEPTH = 3
export const DEFAULT_THRESHOLD = 0.4
export const SECURITY_DEPRECATION_KEYWORDS = ['security', 'vulnerability', 'cve'] as const
export const RISK_SIGNAL_WEIGHTS = {
low: 0.08,
medium: 0.16,
Expand Down Expand Up @@ -187,7 +187,5 @@ export function hasSecurityDeprecationLanguage(message: string | null): boolean
return false
}

const normalizedMessage = message.toLowerCase()

return SECURITY_DEPRECATION_KEYWORDS.some((keyword) => normalizedMessage.includes(keyword))
return isSecurityRelatedDeprecation(message)
}
25 changes: 25 additions & 0 deletions test/security-deprecation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import assert from 'node:assert/strict'
import test from 'node:test'

import { isSecurityRelatedDeprecation } from '../src/domain/security-deprecation.js'

test('shared security-deprecation classifier matches security vulnerability language', () => {
assert.equal(
isSecurityRelatedDeprecation('This version has a security vulnerability. Please upgrade.'),
true,
)
})

test('shared security-deprecation classifier matches structured CVE identifiers', () => {
assert.equal(
isSecurityRelatedDeprecation('See CVE-2025-12345 for details.'),
true,
)
})

test('shared security-deprecation classifier does not match bare cve references', () => {
assert.equal(
isSecurityRelatedDeprecation('See the cve guidance page for more information.'),
false,
)
})
Loading