Skip to content

Commit cf60e7e

Browse files
danielpaulusUbuntuclaude
authored
feat(ssl-assertion): export TlsVersion, CipherSuite, SignatureAlgorithm typed constants (#1392)
* feat(reporters): per-type TRACEROUTE/GRPC/SSL result rendering (P4-CLI-RESULTS) Render failure-debug diagnostics for the three uptime monitor types across every CLI result surface, mirroring the 4.1 public check-results fields: - rest/check-results.ts: typed TracerouteCheckResult/GrpcCheckResult/ SslCheckResult interfaces + additive fields on CheckResult/CheckResultField. - formatters/check-result-detail.ts: per-type terminal + markdown diagnostic block (checks results get) keyed on the typed fields. - reporters/util.ts: GRPC/SSL/TRACEROUTE branches in formatCheckResult (checkly test terminal), sourced from the runner artifact (checkRunData). - formatters/batch-stats.ts: add the three types to TIMING_TYPES. - reporters/json.ts: emit a per-type diagnostics object in the JSON report for a failed run (checkly test --reporter json). Snapshot + assertion tests for each type across all three surfaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(constructs): add GrpcMonitor, SslMonitor, TracerouteMonitor IaC constructs Implement the missing CLI constructs so users can author and deploy gRPC, SSL and Traceroute uptime monitors via checkly/constructs, and so backend CLI export templates that import these classes compile. Constructs follow the DNS/TCP/ICMP monitor pattern (extend Monitor, register with Session, validate, synthesize): - GrpcMonitor / GrpcRequest / GrpcConfig / GrpcMetadata + GrpcAssertionBuilder (RESPONSE_TIME, GRPC_STATUS_CODE, GRPC_HEALTHCHECK_STATUS, GRPC_RESPONSE, GRPC_METADATA). checkType GRPC, top-level degraded/maxResponseTime (<=30000). - SslMonitor / SslRequest / SslConfig / SecurityBaseline + SslAssertionBuilder (cert expiry, chain, hostname, TLS version, cipher, key size, etc). checkType SSL; response-time limits live in sslConfig (degraded/maxResponseTimeMs). - TracerouteMonitor / TracerouteRequest + TracerouteAssertionBuilder (RESPONSE_TIME, HOP_COUNT, PACKET_LOSS). checkType TRACEROUTE, top-level degraded/maxResponseTime (<=30000); port dropped for ICMP probes. Request shapes mirror the public API (verified against checkly-go-sdk types and the p5-parity captured payloads). Wires exports in constructs/index.ts, import codegen in check-codegen.ts (GRPC/SSL/TRACEROUTE -> *MonitorCodegen), and adds the three types to constants.CheckTypes. Tests: construct synthesize/validation/grouping specs, codegen specs (incl. ICMP port-strip and assertion builders), and a regression spec asserting the backend-style export snippets compile against checkly/constructs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(deploy): skip code-bundle upload when nothing was bundled archive.store() (the 'Uploading Playwright tests' step) was called unconditionally on every deploy. For a project of only uptime monitors (GRPC/SSL/TRACEROUTE), the bundler registers no files, so this uploaded an empty Playwright bundle — an unnecessary code-bundle upload in production, and a hard failure in devenv where it 500s on the storage backend. The remote code bundle is consumed only by Playwright check suites (via bundler.marker -> playwright-check.ts); browser checks upload snapshots separately. Add Bundler.isEmpty and skip store() when no files were registered. Refs T65. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(cli): wire gRPC/SSL/Traceroute analytics paths + connection-error rendering Addresses review (Simo) new-monitor checklist gaps: - src/rest/analytics.ts: add checkTypeToPath (grpc-checks/ssl/traceroute) and defaultMetrics for the three types, matching the backend analytics routes + metric registries (gRPC total_*, SSL handshakeTimeMs_*/daysUntilExpiry_avg, TRACEROUTE finalHopLatencyAvg_*/totalHops_avg). - src/reporters/util.ts: add the Connection Error subsection to the GRPC/SSL/ TRACEROUTE result blocks, mirroring ICMP/DNS. * docs(cli): AI context references + examples for gRPC/SSL/Traceroute monitors Addresses review (Simo) new-monitor checklist Phases 5 & 7: - ai-context/references/configure-{grpc,ssl,traceroute}-monitors.md + REFERENCES and EXAMPLE_CONFIGS entries in context.ts (inline exampleConfig). - examples/advanced-project{,-js}/src/__checks__/uptime/{grpc,ssl,traceroute} TS + JS examples with group, assertions, and doc-link comments. * test(cli): e2e deploy fixtures + Create assertions for gRPC/SSL/Traceroute Addresses review (Simo) new-monitor checklist Phase 6: - e2e/__tests__/fixtures/deploy-project/{grpc,ssl,traceroute}.check.ts (activated:false) - deploy.spec.ts: assert the three logical IDs in the Create output. * feat(cli): support TEXT_BODY assertions for gRPC monitors Mirror monorepo #2730 which added TEXT_BODY to grpcMonitorAssertionSources. Adds GrpcAssertionBuilder.textBody() (source TEXT_BODY) + codegen case, matching the API check's textBody() builder. * refactor(cli): restructure SslMonitor props per review (wire shape unchanged) Per @sorccu's review: make SslMonitor ergonomics consistent with other monitors. - degradedResponseTime/maxResponseTime -> construct top level (were in sslConfig) - hostname/port/ipFamily -> request top level (were in sslConfig) - sslClientCertificateId -> into sslConfig (was on request) synthesize() remaps to the identical API wire shape (request.sslConfig.* + request.sslClientCertificateId), so the payload the backend receives is unchanged. Updated codegen (wire->construct), unit + codegen + export-snippet tests, examples (ts+js), e2e fixture, and AI context. * refactor(cli): drop Ms suffix on SslConfig.handshakeTimeout per review @sorccu: we don't suffix timeout properties with Ms even when the value is milliseconds. Renames the construct field handshakeTimeoutMs -> handshakeTimeout; synthesize() and the codegen wire interface keep the API's handshakeTimeoutMs. * refactor(cli): emit SSL degraded/maxResponseTime at top level to match gRPC/traceroute wire * feat(ssl-assertion): export TlsVersion, CipherSuite, SignatureAlgorithm typed constants Add three exported const objects and matching type aliases so callers get IDE autocomplete for the finite value sets: - TlsVersion: TLS1_0/TLS1_1/TLS1_2/TLS1_3 - SignatureAlgorithm: common X.509 algorithm strings (sha256WithRSAEncryption, etc.) - CipherSuite: TLS 1.3 suites + widely-deployed TLS 1.2 ECDHE/RSA suites The GeneralAssertionBuilder API is unchanged; existing string-literal calls continue to compile and work at runtime. * fix(ssl-assertion): real type safety + correct SignatureAlgorithm wire values Blocker 1 — GeneralAssertionBuilder now takes an optional TargetType generic parameter (default: string|number|boolean for full backward compatibility). tlsVersion(), cipherSuite(), signatureAlgorithm() return the narrowed form so .equals() only accepts the typed union — invalid values are rejected at compile time. matches() stays string (it carries a regex, not a value to compare). Blocker 2 — SignatureAlgorithm constants now use Go's x509.Certificate.SignatureAlgorithm.String() output (SHA256-RSA, ECDSA-SHA256, etc.) matching the values the runner evaluates assertions against. New test file ssl-assertion.spec.ts uses @ts-expect-error to prove: - valid constants / string literals are accepted for all three typed builders - invalid strings (wrong format, arbitrary strings, numeric) are rejected - matches() remains unconstrained as intended * fix(ssl-assertion): revert cipherSuite to unconstrained; complete SignatureAlgorithm union cipherSuite() — Go's tls.CipherSuiteName() returns hundreds of IANA names plus 0x.... hex fallbacks; the 15-value union blocked valid values like TLS_RSA_WITH_RC4_128_SHA. Reverted the builder to unconstrained string. The CipherSuite constants remain for autocomplete convenience. SignatureAlgorithm — completed the union to the full Go x509.SignatureAlgorithm.String() set (16 values): added MD2-RSA, MD5-RSA, DSA-SHA1, DSA-SHA256, ECDSA-SHA1. The type now covers every value the runner can emit without blocking any valid input. Updated ssl-assertion.spec.ts: removed @ts-expect-error for cipher strings (now unconstrained), added tests showing arbitrary cipher suite strings compile, and expanded the signatureAlgorithm test to cover all 16 constants. * refactor(cli): drop greaterThanOrEqual and matches from SSL assertions [SIM-295] Remove the GREATER_THAN_OR_EQUAL (`greaterThanOrEqual`) and MATCHES (`matches`) comparison operators from the CLI assertion builders — we are not yet sure we want to keep these assertions. Drops the two methods from the shared Numeric/General assertion builders, the two members from the Comparison union, and the related SSL examples/docs/tests. `tlsVersion`, `keySizeBits`, `cipherSuite` and `signatureAlgorithm` keep `equals`/`notEquals`, which remain backend-valid. The backend (Joi §9 + go-runner) still supports both operators, so this only removes the CLI surface and they can be re-exposed later if we decide to keep them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NC9pESYE5wGqy5TKRzufyc --------- Co-authored-by: Ubuntu <ubuntu@ip-172-31-7-108.eu-central-1.compute.internal> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c66f2f2 commit cf60e7e

3 files changed

Lines changed: 234 additions & 15 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Type-level tests for SslAssertionBuilder typed constants.
3+
*
4+
* These tests verify two things:
5+
* 1. Valid constant / string-literal values are accepted by the typed builders.
6+
* 2. Invalid strings are REJECTED at compile time (via @ts-expect-error) where
7+
* the builder is strictly typed.
8+
*
9+
* If a @ts-expect-error directive becomes "unused" it means the underlying typing
10+
* was loosened — investigate before removing it.
11+
*/
12+
13+
import { describe, it, expect } from 'vitest'
14+
import {
15+
SslAssertionBuilder,
16+
TlsVersion,
17+
CipherSuite,
18+
SignatureAlgorithm,
19+
} from '../index.js'
20+
21+
describe('SslAssertionBuilder — typed target values', () => {
22+
describe('tlsVersion() — strict 4-value union', () => {
23+
it('accepts all TlsVersion constants', () => {
24+
expect(SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_0)).toBeTruthy()
25+
expect(SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_1)).toBeTruthy()
26+
expect(SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_2)).toBeTruthy()
27+
expect(SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_3)).toBeTruthy()
28+
})
29+
30+
it('accepts TLS version string literals directly', () => {
31+
expect(SslAssertionBuilder.tlsVersion().equals('TLS1.2')).toBeTruthy()
32+
expect(SslAssertionBuilder.tlsVersion().equals('TLS1.3')).toBeTruthy()
33+
})
34+
35+
it('rejects an arbitrary string that is not a valid TLS version', () => {
36+
// @ts-expect-error 'not-a-tls-version' is not assignable to TlsVersionValue
37+
SslAssertionBuilder.tlsVersion().equals('not-a-tls-version')
38+
})
39+
40+
it('rejects a numeric TLS version (wrong type)', () => {
41+
// @ts-expect-error number is not assignable to TlsVersionValue
42+
SslAssertionBuilder.tlsVersion().equals(1.3)
43+
})
44+
})
45+
46+
describe('signatureAlgorithm() — complete Go x509.SignatureAlgorithm.String() union', () => {
47+
it('accepts all SignatureAlgorithm constants (Go format)', () => {
48+
// RSA family
49+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.MD2_RSA)).toBeTruthy()
50+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.MD5_RSA)).toBeTruthy()
51+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA1_RSA)).toBeTruthy()
52+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA256_RSA)).toBeTruthy()
53+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA384_RSA)).toBeTruthy()
54+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA512_RSA)).toBeTruthy()
55+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA256_RSAPSS)).toBeTruthy()
56+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA384_RSAPSS)).toBeTruthy()
57+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA512_RSAPSS)).toBeTruthy()
58+
// DSA family
59+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.DSA_SHA1)).toBeTruthy()
60+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.DSA_SHA256)).toBeTruthy()
61+
// ECDSA family
62+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.ECDSA_SHA1)).toBeTruthy()
63+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.ECDSA_SHA256)).toBeTruthy()
64+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.ECDSA_SHA384)).toBeTruthy()
65+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.ECDSA_SHA512)).toBeTruthy()
66+
// EdDSA
67+
expect(SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.ED25519)).toBeTruthy()
68+
})
69+
70+
it('accepts Go-format string literals directly', () => {
71+
expect(SslAssertionBuilder.signatureAlgorithm().equals('SHA256-RSA')).toBeTruthy()
72+
expect(SslAssertionBuilder.signatureAlgorithm().equals('ECDSA-SHA256')).toBeTruthy()
73+
expect(SslAssertionBuilder.signatureAlgorithm().equals('Ed25519')).toBeTruthy()
74+
})
75+
76+
it('rejects OID-style strings (wrong format — the runner uses Go String() not OID names)', () => {
77+
// @ts-expect-error 'sha256WithRSAEncryption' is not assignable to SignatureAlgorithmValue
78+
SslAssertionBuilder.signatureAlgorithm().equals('sha256WithRSAEncryption')
79+
})
80+
81+
it('rejects arbitrary strings', () => {
82+
// @ts-expect-error 'invalid-alg' is not assignable to SignatureAlgorithmValue
83+
SslAssertionBuilder.signatureAlgorithm().equals('invalid-alg')
84+
})
85+
})
86+
87+
describe('cipherSuite() — intentionally unconstrained (open-ended IANA set)', () => {
88+
it('accepts CipherSuite constants for common suites', () => {
89+
expect(SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_256_GCM_SHA384)).toBeTruthy()
90+
expect(SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)).toBeTruthy()
91+
})
92+
93+
it('accepts any string — including suites not in the CipherSuite constant list', () => {
94+
// These are valid Go tls.CipherSuiteName() outputs that exceed the short constant list
95+
expect(SslAssertionBuilder.cipherSuite().equals('TLS_RSA_WITH_RC4_128_SHA')).toBeTruthy()
96+
expect(SslAssertionBuilder.cipherSuite().equals('TLS_RSA_WITH_3DES_EDE_CBC_SHA')).toBeTruthy()
97+
expect(SslAssertionBuilder.cipherSuite().notEquals('TLS_RSA_WITH_RC4_128_SHA')).toBeTruthy()
98+
expect(SslAssertionBuilder.cipherSuite().equals('TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256')).toBeTruthy()
99+
})
100+
})
101+
})

packages/cli/src/constructs/internal/assertion.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,19 @@ export class NumericAssertionBuilder<Source extends string, Property extends str
5959
}
6060
}
6161

62-
export class GeneralAssertionBuilder<Source extends string> {
62+
/**
63+
* General assertion builder supporting string / number / boolean targets.
64+
*
65+
* The optional `TargetType` parameter narrows the value-comparison methods
66+
* (`equals`, `notEquals`, `lessThan`, `greaterThan`)
67+
* to a specific set of values — e.g. `TlsVersionValue` for TLS version
68+
* assertions. When omitted it defaults to `string | number | boolean`,
69+
* preserving full backward compatibility.
70+
*/
71+
export class GeneralAssertionBuilder<
72+
Source extends string,
73+
TargetType extends string | number | boolean = string | number | boolean,
74+
> {
6375
source: Source
6476
property?: string
6577
regex?: string
@@ -70,11 +82,11 @@ export class GeneralAssertionBuilder<Source extends string> {
7082
this.regex = regex
7183
}
7284

73-
equals (target: string | number | boolean): Assertion<Source> {
85+
equals (target: TargetType): Assertion<Source> {
7486
return this._toAssertion('EQUALS', target)
7587
}
7688

77-
notEquals (target: string | number | boolean): Assertion<Source> {
89+
notEquals (target: TargetType): Assertion<Source> {
7890
return this._toAssertion('NOT_EQUALS', target)
7991
}
8092

@@ -86,11 +98,11 @@ export class GeneralAssertionBuilder<Source extends string> {
8698
return this._toAssertion('NOT_HAS_KEY', target)
8799
}
88100

89-
hasValue (target: string | number | boolean): Assertion<Source> {
101+
hasValue (target: TargetType): Assertion<Source> {
90102
return this._toAssertion('HAS_VALUE', target)
91103
}
92104

93-
notHasValue (target: string | number | boolean): Assertion<Source> {
105+
notHasValue (target: TargetType): Assertion<Source> {
94106
return this._toAssertion('NOT_HAS_VALUE', target)
95107
}
96108

@@ -102,11 +114,11 @@ export class GeneralAssertionBuilder<Source extends string> {
102114
return this._toAssertion('NOT_EMPTY')
103115
}
104116

105-
lessThan (target: string | number | boolean): Assertion<Source> {
117+
lessThan (target: TargetType): Assertion<Source> {
106118
return this._toAssertion('LESS_THAN', target)
107119
}
108120

109-
greaterThan (target: string | number | boolean): Assertion<Source> {
121+
greaterThan (target: TargetType): Assertion<Source> {
110122
return this._toAssertion('GREATER_THAN', target)
111123
}
112124

packages/cli/src/constructs/ssl-assertion.ts

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,91 @@
11
import { Assertion as CoreAssertion, NumericAssertionBuilder, GeneralAssertionBuilder } from './internal/assertion.js'
22

3+
/**
4+
* Known TLS protocol versions for use with {@link SslAssertionBuilder.tlsVersion}.
5+
*
6+
* @example
7+
* ```typescript
8+
* SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_3)
9+
* ```
10+
*/
11+
export const TlsVersion = {
12+
TLS1_0: 'TLS1.0',
13+
TLS1_1: 'TLS1.1',
14+
TLS1_2: 'TLS1.2',
15+
TLS1_3: 'TLS1.3',
16+
} as const
17+
18+
export type TlsVersionValue = (typeof TlsVersion)[keyof typeof TlsVersion]
19+
20+
/**
21+
* Signature algorithms as reported by Go's `x509.Certificate.SignatureAlgorithm.String()`.
22+
* These are the exact values the SSL runner evaluates assertions against — use these
23+
* constants (or the string literals they represent) with {@link SslAssertionBuilder.signatureAlgorithm}.
24+
*
25+
* @example
26+
* ```typescript
27+
* SslAssertionBuilder.signatureAlgorithm().equals(SignatureAlgorithm.SHA256_RSA)
28+
* ```
29+
*/
30+
export const SignatureAlgorithm = {
31+
// RSA
32+
MD2_RSA: 'MD2-RSA',
33+
MD5_RSA: 'MD5-RSA',
34+
SHA1_RSA: 'SHA1-RSA',
35+
SHA256_RSA: 'SHA256-RSA',
36+
SHA384_RSA: 'SHA384-RSA',
37+
SHA512_RSA: 'SHA512-RSA',
38+
SHA256_RSAPSS: 'SHA256-RSAPSS',
39+
SHA384_RSAPSS: 'SHA384-RSAPSS',
40+
SHA512_RSAPSS: 'SHA512-RSAPSS',
41+
// DSA
42+
DSA_SHA1: 'DSA-SHA1',
43+
DSA_SHA256: 'DSA-SHA256',
44+
// ECDSA
45+
ECDSA_SHA1: 'ECDSA-SHA1',
46+
ECDSA_SHA256: 'ECDSA-SHA256',
47+
ECDSA_SHA384: 'ECDSA-SHA384',
48+
ECDSA_SHA512: 'ECDSA-SHA512',
49+
// EdDSA
50+
ED25519: 'Ed25519',
51+
} as const
52+
53+
export type SignatureAlgorithmValue = (typeof SignatureAlgorithm)[keyof typeof SignatureAlgorithm]
54+
55+
/**
56+
* Commonly-used IANA cipher suite names for use with
57+
* {@link SslAssertionBuilder.cipherSuite}. Includes TLS 1.3 suites and
58+
* widely-deployed TLS 1.2 suites.
59+
*
60+
* @example
61+
* ```typescript
62+
* SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_256_GCM_SHA384)
63+
* SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
64+
* ```
65+
*/
66+
export const CipherSuite = {
67+
// TLS 1.3
68+
TLS_AES_128_GCM_SHA256: 'TLS_AES_128_GCM_SHA256',
69+
TLS_AES_256_GCM_SHA384: 'TLS_AES_256_GCM_SHA384',
70+
TLS_CHACHA20_POLY1305_SHA256: 'TLS_CHACHA20_POLY1305_SHA256',
71+
// TLS 1.2 — ECDHE (forward-secret, preferred)
72+
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
73+
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
74+
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
75+
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
76+
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256',
77+
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256',
78+
// TLS 1.2 — RSA key exchange
79+
TLS_RSA_WITH_AES_128_GCM_SHA256: 'TLS_RSA_WITH_AES_128_GCM_SHA256',
80+
TLS_RSA_WITH_AES_256_GCM_SHA384: 'TLS_RSA_WITH_AES_256_GCM_SHA384',
81+
TLS_RSA_WITH_AES_128_CBC_SHA256: 'TLS_RSA_WITH_AES_128_CBC_SHA256',
82+
TLS_RSA_WITH_AES_256_CBC_SHA256: 'TLS_RSA_WITH_AES_256_CBC_SHA256',
83+
TLS_RSA_WITH_AES_128_CBC_SHA: 'TLS_RSA_WITH_AES_128_CBC_SHA',
84+
TLS_RSA_WITH_AES_256_CBC_SHA: 'TLS_RSA_WITH_AES_256_CBC_SHA',
85+
} as const
86+
87+
export type CipherSuiteValue = (typeof CipherSuite)[keyof typeof CipherSuite]
88+
389
type SslAssertionSource =
490
| 'CERT_EXPIRES_IN_DAYS'
591
| 'CERT_NOT_EXPIRED'
@@ -12,6 +98,7 @@ type SslAssertionSource =
1298
| 'ISSUER_FINGERPRINT_SHA256'
1399
| 'KEY_SIZE_BITS'
14100
| 'SIGNATURE_ALGORITHM'
101+
| 'OCSP_STAPLED'
15102

16103
export type SslAssertion = CoreAssertion<SslAssertionSource>
17104

@@ -77,17 +164,24 @@ export class SslAssertionBuilder {
77164

78165
/**
79166
* Creates an assertion builder for the negotiated TLS version.
80-
* @returns A general assertion builder for the TLS version.
167+
* The `.equals()` method accepts only the
168+
* known TLS version strings — use the {@link TlsVersion} constants or
169+
* the string literals `'TLS1.0'`…`'TLS1.3'`.
170+
* @returns A typed assertion builder for the TLS version.
81171
*/
82-
static tlsVersion () {
83-
return new GeneralAssertionBuilder<SslAssertionSource>('TLS_VERSION')
172+
static tlsVersion (): GeneralAssertionBuilder<SslAssertionSource, TlsVersionValue> {
173+
return new GeneralAssertionBuilder<SslAssertionSource, TlsVersionValue>('TLS_VERSION')
84174
}
85175

86176
/**
87177
* Creates an assertion builder for the negotiated cipher suite.
88-
* @returns A general assertion builder for the cipher suite.
178+
* Go's `tls.CipherSuiteName()` can return hundreds of IANA names plus
179+
* `0x....` hex fallbacks, so this builder is intentionally unconstrained.
180+
* Use the {@link CipherSuite} constants for common suites (autocomplete),
181+
* or pass any string literal / regex pattern as needed.
182+
* @returns An unconstrained assertion builder for the cipher suite.
89183
*/
90-
static cipherSuite () {
184+
static cipherSuite (): GeneralAssertionBuilder<SslAssertionSource> {
91185
return new GeneralAssertionBuilder<SslAssertionSource>('CIPHER_SUITE')
92186
}
93187

@@ -117,9 +211,21 @@ export class SslAssertionBuilder {
117211

118212
/**
119213
* Creates an assertion builder for the certificate signature algorithm.
120-
* @returns A general assertion builder for the signature algorithm.
214+
* Values are Go's `x509.Certificate.SignatureAlgorithm.String()` output
215+
* (e.g. `'SHA256-RSA'`, `'ECDSA-SHA256'`). Use the {@link SignatureAlgorithm}
216+
* constants or those string literals with `.equals()`.
217+
* @returns A typed assertion builder for the signature algorithm.
218+
*/
219+
static signatureAlgorithm (): GeneralAssertionBuilder<SslAssertionSource, SignatureAlgorithmValue> {
220+
return new GeneralAssertionBuilder<SslAssertionSource, SignatureAlgorithmValue>('SIGNATURE_ALGORITHM')
221+
}
222+
223+
/**
224+
* Creates an assertion builder for whether a stapled OCSP response was
225+
* provided during the handshake.
226+
* @returns A general assertion builder for the OCSP-stapled status.
121227
*/
122-
static signatureAlgorithm () {
123-
return new GeneralAssertionBuilder<SslAssertionSource>('SIGNATURE_ALGORITHM')
228+
static ocspStapled () {
229+
return new GeneralAssertionBuilder<SslAssertionSource>('OCSP_STAPLED')
124230
}
125231
}

0 commit comments

Comments
 (0)