Skip to content

Commit eaf5809

Browse files
danielpaulusclaude
andauthored
fix(cli): tidy SSL baseline block in checks get --result [SIM-291] (#1405)
Three result-rendering fixes to the SSL security-baseline block: - Colour the baseline verdict correctly: it was compared to 'PASS' (uppercase) so a lowercase 'pass' always fell through to red. Normalise and map pass→green / warn→yellow / fail→red. - Drop the per-rule config-time severity suffix ((fail)/(ignore)) — it's a configuration concern, confusing in a result (a green ✔ next to "fail"). - Show the observed value per rule (e.g. "min TLS version: TLS1.3", "min key size: 256-bit", "weak cipher suite: <suite>") pulled from the SSL response; rules without a single scalar value (known bad CA, SCT present) stay name-only. Snapshots + assertions updated; all check-result-detail tests pass. Claude-Session: https://claude.ai/code/session_01NC9pESYE5wGqy5TKRzufyc Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent cdf176f commit eaf5809

3 files changed

Lines changed: 64 additions & 33 deletions

File tree

packages/cli/src/formatters/__tests__/__snapshots__/check-result-detail.spec.ts.snap

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,15 @@ exports[`formatResultDetail > SSL check result > renders markdown snapshot > ssl
317317
- **OCSP stapled:** no
318318
319319
## Security Baseline
320-
- ✔ min TLS version (fail)
321-
- ✔ min key size (fail)
322-
- ✔ weak signature algorithm (fail)
323-
- ✖ weak cipher suite (fail)
324-
- ✔ known bad CA (fail)
325-
- ✔ recommended TLS version (ignore)
326-
- ✔ recommended key size (ignore)
327-
- ✔ OCSP must-staple respected (ignore)
328-
- ✔ SCT present (ignore)
320+
- ✔ min TLS version: TLS 1.3
321+
- ✔ min key size: 256-bit
322+
- ✔ weak signature algorithm: ECDSA-SHA256
323+
- ✖ weak cipher suite: TLS_AES_256_GCM_SHA384
324+
- ✔ known bad CA
325+
- ✔ recommended TLS version: TLS 1.3
326+
- ✔ recommended key size: 256-bit
327+
- ✔ OCSP must-staple respected: not stapled
328+
- ✔ SCT present
329329
330330
## Assertions
331331
- ✖ CERT_EXPIRES_IN_DAYS is greater than target "99999". Received: 52.
@@ -366,15 +366,15 @@ Serial: 35428337808578903465180920265426569102
366366
OCSP stapled: no
367367
368368
SECURITY BASELINE
369-
✔ min TLS version (fail)
370-
✔ min key size (fail)
371-
✔ weak signature algorithm (fail)
372-
✖ weak cipher suite (fail)
373-
✔ known bad CA (fail)
374-
✔ recommended TLS version (ignore)
375-
✔ recommended key size (ignore)
376-
✔ OCSP must-staple respected (ignore)
377-
✔ SCT present (ignore)
369+
✔ min TLS version: TLS 1.3
370+
✔ min key size: 256-bit
371+
✔ weak signature algorithm: ECDSA-SHA256
372+
✖ weak cipher suite: TLS_AES_256_GCM_SHA384
373+
✔ known bad CA
374+
✔ recommended TLS version: TLS 1.3
375+
✔ recommended key size: 256-bit
376+
✔ OCSP must-staple respected: not stapled
377+
✔ SCT present
378378
379379
ASSERTIONS
380380
✖ CERT_EXPIRES_IN_DAYS is greater than target "99999". Received: 52.

packages/cli/src/formatters/__tests__/check-result-detail.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,19 +579,19 @@ describe('formatResultDetail', () => {
579579
it('renders the per-rule security baseline (terminal)', () => {
580580
const result = stripAnsi(formatResultDetail(sslCheckResult, 'terminal'))
581581
expect(result).toContain('SECURITY BASELINE')
582-
expect(result).toContain(`${PASS} min TLS version (fail)`)
583-
expect(result).toContain(`${FAIL} weak cipher suite (fail)`)
584-
expect(result).toContain(`${PASS} recommended TLS version (ignore)`)
582+
expect(result).toContain(`${PASS} min TLS version`)
583+
expect(result).toContain(`${FAIL} weak cipher suite`)
584+
expect(result).toContain(`${PASS} recommended TLS version`)
585585
// The one-line summary is still present in the RESULT block.
586586
expect(result).toContain('Baseline:')
587587
})
588588

589589
it('renders the per-rule security baseline (markdown)', () => {
590590
const result = formatResultDetail(sslCheckResult, 'md')
591591
expect(result).toContain('## Security Baseline')
592-
expect(result).toContain(`- ${PASS} min TLS version (fail)`)
593-
expect(result).toContain(`- ${FAIL} weak cipher suite (fail)`)
594-
expect(result).toContain(`- ${PASS} SCT present (ignore)`)
592+
expect(result).toContain(`- ${PASS} min TLS version`)
593+
expect(result).toContain(`- ${FAIL} weak cipher suite`)
594+
expect(result).toContain(`- ${PASS} SCT present`)
595595
expect(result).toBe(stripAnsi(result))
596596
})
597597

packages/cli/src/formatters/check-result-detail.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,12 +1085,39 @@ function appendSslCertificateMd (lines: string[], resp: Record<string, unknown>)
10851085
// tuples so terminal and markdown can render the same rules with format-specific
10861086
// styling. A rule that carries neither a `violated` flag nor a scalar value is
10871087
// skipped.
1088+
// Observed value for a baseline rule, pulled from the SSL response so the result
1089+
// shows *what* was seen (e.g. "min TLS version: TLS1.3"), not just the rule name.
1090+
// Rules without a single observed value (knownBadCA, sctPresent) return undefined.
1091+
function sslBaselineObservedValue (key: string, resp: Record<string, unknown>): string | undefined {
1092+
const cert = asObject(resp.certificate)
1093+
switch (key) {
1094+
case 'minTLSVersion':
1095+
case 'recommendedTLSVersion':
1096+
return str(resp.protocol)
1097+
case 'minKeySizeBits':
1098+
case 'recommendedKeySizeBits': {
1099+
const bits = cert ? num(cert.keySizeBits) : undefined
1100+
return bits != null ? `${bits}-bit` : undefined
1101+
}
1102+
case 'weakSignatureAlgorithm':
1103+
return cert ? str(cert.signatureAlgorithm) : undefined
1104+
case 'weakCipherSuite':
1105+
return str(resp.cipherSuite)
1106+
case 'ocspMustStapleRespected': {
1107+
const stapled = boolFlag(resp.ocspStapled)
1108+
return stapled == null ? undefined : (stapled ? 'stapled' : 'not stapled')
1109+
}
1110+
default:
1111+
return undefined
1112+
}
1113+
}
1114+
10881115
function sslBaselineRules (
10891116
resp: Record<string, unknown>,
1090-
): Array<{ human: string, violated?: boolean, severity?: string, scalar?: string }> {
1117+
): Array<{ human: string, violated?: boolean, severity?: string, scalar?: string, value?: string }> {
10911118
const baseline = asObject(resp.securityBaseline)
10921119
if (!baseline) return []
1093-
const rules: Array<{ human: string, violated?: boolean, severity?: string, scalar?: string }> = []
1120+
const rules: Array<{ human: string, violated?: boolean, severity?: string, scalar?: string, value?: string }> = []
10941121
for (const [key, human] of SSL_BASELINE_RULES) {
10951122
const rule = baseline[key]
10961123
if (rule == null) continue
@@ -1099,7 +1126,7 @@ function sslBaselineRules (
10991126
const violated = boolFlag(obj.violated)
11001127
const severity = str(obj.severity)
11011128
if (violated == null && severity == null) continue
1102-
rules.push({ human, violated, severity })
1129+
rules.push({ human, violated, severity, value: sslBaselineObservedValue(key, resp) })
11031130
} else if (typeof rule === 'string' || typeof rule === 'number' || typeof rule === 'boolean') {
11041131
rules.push({ human, scalar: String(rule) })
11051132
}
@@ -1114,16 +1141,16 @@ function appendSslBaselineTerminal (lines: string[], resp: Record<string, unknow
11141141
lines.push(heading('SECURITY BASELINE', 2, 'terminal'))
11151142
for (const rule of rules) {
11161143
if (rule.violated == null && rule.scalar == null) {
1117-
lines.push(` ${chalk.dim('·')} ${rule.human}${rule.severity ? chalk.dim(` (${rule.severity})`) : ''}`)
1144+
lines.push(` ${chalk.dim('·')} ${rule.human}`)
11181145
continue
11191146
}
11201147
if (rule.scalar != null) {
11211148
lines.push(` ${chalk.dim('·')} ${rule.human}: ${rule.scalar}`)
11221149
continue
11231150
}
1124-
const head = `${rule.violated ? assertionSymbols.error : assertionSymbols.success} ${rule.human}`
1151+
const head = `${rule.violated ? assertionSymbols.error : assertionSymbols.success} ${rule.human}${rule.value ? `: ${rule.value}` : ''}`
11251152
const coloredHead = rule.violated ? chalk.red(head) : chalk.green(head)
1126-
lines.push(` ${coloredHead}${rule.severity ? chalk.dim(` (${rule.severity})`) : ''}`)
1153+
lines.push(` ${coloredHead}`)
11271154
}
11281155
}
11291156

@@ -1138,11 +1165,11 @@ function appendSslBaselineMd (lines: string[], resp: Record<string, unknown>): v
11381165
continue
11391166
}
11401167
if (rule.violated == null) {
1141-
lines.push(`- ${rule.human}${rule.severity ? ` (${rule.severity})` : ''}`)
1168+
lines.push(`- ${rule.human}`)
11421169
continue
11431170
}
11441171
const sym = rule.violated ? assertionSymbols.error : assertionSymbols.success
1145-
lines.push(`- ${sym} ${rule.human}${rule.severity ? ` (${rule.severity})` : ''}`)
1172+
lines.push(`- ${sym} ${rule.human}${rule.value ? `: ${rule.value}` : ''}`)
11461173
}
11471174
}
11481175

@@ -1176,7 +1203,11 @@ function formatSslResultTerminal (ssl: SslCheckResult): string[] {
11761203
const verdict = str(ssl.baselineVerdict)
11771204
const grade = str(ssl.baselineGrade)
11781205
if (verdict || grade) {
1179-
const verdictStr = verdict ? (verdict === 'PASS' ? chalk.green(verdict) : chalk.red(verdict)) : ''
1206+
let verdictStr = ''
1207+
if (verdict) {
1208+
const v = verdict.toLowerCase()
1209+
verdictStr = v === 'pass' ? chalk.green(verdict) : v === 'warn' ? chalk.yellow(verdict) : chalk.red(verdict)
1210+
}
11801211
lines.push(`${label('Baseline:')}${[verdictStr, grade ? `grade ${grade}` : ''].filter(Boolean).join(' ')}`)
11811212
}
11821213

0 commit comments

Comments
 (0)