Skip to content

Commit ef6111a

Browse files
authored
fix: retune defensive precision followups (#83)
## Summary This PR retunes the next batch of CodeGuard precision false positives from TypeScript/JavaScript API and integration code. The goal is to keep the production-readiness checks useful while avoiding noisy findings for patterns that are already bounded, typed, or intentionally surfaced to callers. ### What changed - Retuned `defensive.integer-overflow`. - Stops treating `count + 1` external ID allocation as numeric overflow. - Skips seed/script paths and date/count formatting arithmetic. - Keeps actual unsafe size arithmetic covered. - Refined `defensive.sequence-collision-risk`. - `count + 1` external ID allocation is now reported under sequence-collision risk instead of integer overflow. - Bounded Prisma `P2002` / unique-collision retry is treated as mitigation, not full resolution. - Retry-mitigated cases emit a lower-confidence architectural-debt warning recommending a database sequence, UUID, or transactional allocator. - Retuned `defensive.bounds-assumption`. - Narrows the rule to sequence-like indexing. - Avoids dictionary/object/env access such as `fieldMap[name]` and `process.env[name]`. - Retuned `defensive.missing-resource-limit`. - Credits Prisma `take`. - Credits bounded constants and pre-`formData()` content-length helper guards. - Keeps truly unbounded request/form reads covered. - Retuned `function.inconsistent-return-contract`. - Allows parser, lookup, read, resolve, and extraction helpers with explicit nullable contracts such as `T | null`. - Allows `exists()` true/false contracts. - Corrects `return false` so it is no longer treated as an empty return. - Retuned `error.partial-failure-hidden`. - Allows digest/fetch loops that append diagnostics/errors/failures and return those diagnostics to callers. - Keeps silent `catch`/`continue` paths covered. - Updated user-facing metadata. - Updated rule catalog, fix-template guidance, and `docs/checks.md` for the retry-mitigated sequence-debt behavior. ### Tests - Added regression coverage for retry-wrapped external ID allocation. - Added regression coverage for seed/script and date bucket arithmetic. - Added regression coverage for dictionary/env indexing vs array indexing. - Added regression coverage for Prisma `take` and pre-`formData()` content-length helper guards. - Added regression coverage for nullable parser/lookup/exists return contracts. - Added regression coverage for surfaced diagnostics vs hidden partial failures. ## Validation - Focused regression tests - `go test ./tests/checks -count=1` - `go test ./...` - `golangci-lint run` - `make codeguard-ci`
2 parents 02a2f72 + 4d7dfa9 commit ef6111a

9 files changed

Lines changed: 362 additions & 26 deletions

docs/checks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ These rules live outside the repository-wide `Change Safety` section in report o
11861186
| Defensive programming | `defensive.invalid-state-representable` | warn | Booleans or raw status strings can represent impossible state combinations. |
11871187
| Defensive programming | `defensive.null-assumption` | warn | Nullable boundary values are dereferenced without a nil/null guard. |
11881188
| Defensive programming | `defensive.integer-overflow` | warn | Arithmetic on count, size, or length input lacks an overflow bound check. |
1189-
| Defensive programming | `defensive.sequence-collision-risk` | warn | External ID allocation derives the next value from current count without guarded unique-collision retry. |
1189+
| Defensive programming | `defensive.sequence-collision-risk` | warn | External ID allocation derives the next value from current count; bounded unique-collision retry is treated as mitigation, not full resolution. |
11901190
| Defensive programming | `defensive.bounds-assumption` | warn | Indexed access assumes collection bounds without a nearby length check. |
11911191
| Defensive programming | `defensive.unsafe-default` | warn | A config/env fallback can fail open or disable a safety control. |
11921192
| Defensive programming | `defensive.non-exhaustive-branch` | warn | Enum-like state/kind/type branching lacks default or exhaustive handling. |

internal/codeguard/checks/quality/quality_defensive.go

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ const (
2525
)
2626

2727
var (
28-
indexAccessPattern = regexp.MustCompile(`\b([A-Za-z_][\w$]*(?:\.[A-Za-z_][\w$]*)?)\s*\[\s*(?:0|[A-Za-z_][\w$]*)\s*\]`)
28+
indexAccessPattern = regexp.MustCompile(`\b([A-Za-z_][\w$]*(?:\.[A-Za-z_][\w$]*)?)\s*\[\s*([^\]\n]+)\s*\]`)
2929
jsonDecodePattern = regexp.MustCompile(`(?i)(json\.Unmarshal|json\.NewDecoder|JSON\.parse|json\.loads|nlohmann::json::parse|decode_json|parseJson)`)
3030
externalCallPattern = regexp.MustCompile(`(?i)(http\.Get|client\.Do|fetch\s*\(|axios\.|requests\.(get|post|put|delete)|curl_easy_perform|httplib::|http_client)`)
31-
resourceReadPattern = regexp.MustCompile(`(?i)(io\.ReadAll|ReadAll|read_to_string|read_to_end|\.read\s*\(|bodyParser|multer|upload|request\.body|r\.Body)`)
31+
resourceReadPattern = regexp.MustCompile(`(?i)(io\.ReadAll|ReadAll|read_to_string|read_to_end|\.read\s*\(|bodyParser|multer|upload|formData\s*\(|request\.body|r\.Body|findMany\s*\(|findFirst\s*\()`)
3232
unsafeDefaultPattern = regexp.MustCompile(`(?i)(getenv|process\.env|os\.environ|std::getenv|config).*?(default|fallback|\|\||!=|,\s*['"]).*?(true|false|allow|disable|skip|insecure)`)
3333
switchLikePattern = regexp.MustCompile(`(?i)\b(switch|match)\b[^{:\n]*(status|state|kind|type)`)
3434
stateAssignmentPattern = regexp.MustCompile(`(?i)(status|state)\s*(?:=|:=|=>)\s*["']?(paid|active|complete|completed|shipped|deleted|approved)["']?`)
@@ -40,6 +40,7 @@ var (
4040
resourceNamedCountLimit = regexp.MustCompile(`(?i)\b(?:max|limit|quota|cap)[A-Za-z0-9_]*(?:count|size|length|len|bytes)\b`)
4141
sequenceAllocationLine = regexp.MustCompile(`(?i)\b(?:external[_]?id|next[_]?id|sequence|slug|number)\b.*(?:count|max)\s*\+\s*1|(?:count|max)\s*\+\s*1.*\b(?:external[_]?id|next[_]?id|sequence|slug|number)\b`)
4242
jsonReaderSchemaCall = regexp.MustCompile(`(?i)\b(?:read|parse|decode)Json[A-Za-z0-9_]*\s*\([^)\n,]+,\s*[A-Za-z_$][\w$]*(?:Schema|Validator|Codec|Parser)\b`)
43+
prismaTakePattern = regexp.MustCompile(`(?is)\b(?:findMany|findFirst|findUnique|query|search)\s*\([^)]*\btake\s*:`)
4344
)
4445

4546
func defensiveBoundaryFindings(env support.Context, file string, fn precisionFunction) []core.Finding {
@@ -58,9 +59,9 @@ func defensiveBoundaryFindings(env support.Context, file string, fn precisionFun
5859
findings = append(findings, precisionWarnFinding(env, defensiveNullAssumptionRuleID, file, line,
5960
"nullable boundary value is dereferenced without a nil/null guard", core.ConfidenceMedium))
6061
}
61-
if line, ok := sequenceCollisionRiskLine(fn, loweredBody); ok {
62+
if line, message, confidence, ok := sequenceCollisionRiskLine(file, fn, loweredBody); ok {
6263
findings = append(findings, precisionWarnFinding(env, defensiveSequenceCollisionRiskRuleID, file, line,
63-
"external ID allocation derives the next value from current count without guarded unique-collision retry", core.ConfidenceMedium))
64+
message, confidence))
6465
}
6566
if line, ok := integerOverflowLine(file, fn, loweredBody); ok {
6667
findings = append(findings, precisionWarnFinding(env, defensiveIntegerOverflowRuleID, file, line,
@@ -230,7 +231,7 @@ func firstUseLine(fn precisionFunction, name string) int {
230231
}
231232

232233
func integerOverflowLine(file string, fn precisionFunction, loweredBody string) (int, bool) {
233-
if isUIRenderArithmeticContext(file, fn, loweredBody) {
234+
if isUIRenderArithmeticContext(file, fn, loweredBody) || isSeedOrScriptSourcePath(file) {
234235
return 0, false
235236
}
236237
if sequenceAllocationArithmetic(loweredBody) || metricStatArithmeticContext(fn, loweredBody) || dateCountFormattingContext(fn, loweredBody) {
@@ -248,11 +249,21 @@ func integerOverflowLine(file string, fn precisionFunction, loweredBody string)
248249
return 0, false
249250
}
250251

251-
func sequenceCollisionRiskLine(fn precisionFunction, loweredBody string) (int, bool) {
252-
if !sequenceAllocationArithmetic(loweredBody) || guardedSequenceCollisionRetry(loweredBody) {
253-
return 0, false
252+
func sequenceCollisionRiskLine(file string, fn precisionFunction, loweredBody string) (int, string, string, bool) {
253+
if isSeedOrScriptSourcePath(file) || !sequenceAllocationArithmetic(loweredBody) {
254+
return 0, "", core.ConfidenceLow, false
255+
}
256+
line := firstSequenceAllocationLine(fn)
257+
if guardedSequenceCollisionRetry(loweredBody) {
258+
return line,
259+
"external ID allocation is protected by bounded unique-collision retry, but count-derived IDs remain architectural debt; prefer a database sequence or transactional allocator",
260+
core.ConfidenceLow,
261+
true
254262
}
255-
return firstSequenceAllocationLine(fn), true
263+
return line,
264+
"external ID allocation derives the next value from current count; use a database sequence, UUID, or transactional allocator instead of count-based generation",
265+
core.ConfidenceMedium,
266+
true
256267
}
257268

258269
func sequenceAllocationArithmetic(loweredBody string) bool {
@@ -273,7 +284,7 @@ func firstSequenceAllocationLine(fn precisionFunction) int {
273284
}
274285

275286
func guardedSequenceCollisionRetry(loweredBody string) bool {
276-
if containsAny(loweredBody, []string{"withexternalidretry", "with_external_id_retry"}) &&
287+
if containsAny(loweredBody, []string{"withexternalidretry", "with_external_id_retry", "externalidretry", "external_id_retry"}) &&
277288
containsAny(loweredBody, []string{"p2002", "unique", "collision", "externalid", "external_id"}) {
278289
return true
279290
}
@@ -296,12 +307,13 @@ func metricStatArithmeticContext(fn precisionFunction, loweredBody string) bool
296307

297308
func dateCountFormattingContext(fn precisionFunction, loweredBody string) bool {
298309
loweredName := strings.ToLower(fn.Name)
299-
if !containsAny(loweredName, []string{"format", "display", "label", "render", "summary", "calendar", "date", "time"}) {
310+
if !containsAny(loweredName, []string{"format", "display", "label", "render", "summary", "calendar", "date", "time", "bucket", "group"}) {
300311
return false
301312
}
302313
return containsAny(loweredBody, []string{
303314
"date", "time", "calendar", "duration", "intl.", "datetimeformat", "formatdistance",
304-
"formatrelative", "plural", "label", "title", "subtitle", "`${", " + \"", " + '",
315+
"formatrelative", "plural", "label", "title", "subtitle", "bucket", "startof", "endof",
316+
"adddays", "subdays", "dayjs", "date-fns", "`${", " + \"", " + '",
305317
})
306318
}
307319

@@ -321,6 +333,24 @@ func isUIRenderArithmeticContext(file string, fn precisionFunction, loweredBody
321333
})
322334
}
323335

336+
func isSeedOrScriptSourcePath(file string) bool {
337+
normalized := strings.ToLower(strings.ReplaceAll(file, "\\", "/"))
338+
base := normalized
339+
if slash := strings.LastIndex(base, "/"); slash >= 0 {
340+
base = base[slash+1:]
341+
}
342+
return strings.Contains(normalized, "/scripts/") ||
343+
strings.Contains(normalized, "/script/") ||
344+
strings.Contains(normalized, "/seed") ||
345+
strings.Contains(normalized, "/seeds/") ||
346+
strings.Contains(normalized, "/backfill") ||
347+
strings.Contains(normalized, "/import") ||
348+
strings.HasPrefix(base, "seed") ||
349+
strings.HasPrefix(base, "backfill") ||
350+
strings.HasPrefix(base, "import") ||
351+
strings.HasPrefix(base, "cleanup")
352+
}
353+
324354
func boundsAssumptionLine(fn precisionFunction, loweredBody string) (int, bool) {
325355
if containsAny(loweredBody, []string{"len(", ".length", ".size()", "empty()", "bounds", "range", "count >"}) {
326356
return 0, false
@@ -334,16 +364,38 @@ func boundsAssumptionLine(fn precisionFunction, loweredBody string) (int, bool)
334364
if match == nil {
335365
continue
336366
}
337-
if nearbyBoundsGuard(fn.Statements, idx, match[1]) {
367+
if !indexExpressionLooksSequenceAccess(match[1], match[2], raw) {
338368
continue
339369
}
340-
if indexAccessPattern.MatchString(raw) {
341-
return statement.Line, true
370+
if nearbyBoundsGuard(fn.Statements, idx, match[1]) {
371+
continue
342372
}
373+
return statement.Line, true
343374
}
344375
return 0, false
345376
}
346377

378+
func indexExpressionLooksSequenceAccess(target string, key string, raw string) bool {
379+
loweredTarget := strings.ToLower(strings.TrimSpace(target))
380+
loweredKey := strings.ToLower(strings.TrimSpace(strings.Trim(key, `"'`)))
381+
loweredRaw := strings.ToLower(raw)
382+
if strings.Contains(loweredTarget, "process.env") || strings.Contains(loweredTarget, "env") ||
383+
strings.Contains(loweredTarget, "map") || strings.Contains(loweredTarget, "dict") || strings.Contains(loweredTarget, "lookup") {
384+
return false
385+
}
386+
if containsAny(loweredRaw, []string{"record<", "map<", "dictionary", "dict", "object.", "hasown", " in ", ".has("}) {
387+
return false
388+
}
389+
if regexp.MustCompile(`^\d+$`).MatchString(loweredKey) {
390+
return true
391+
}
392+
if containsAny(loweredKey, []string{"index", "idx", "offset", "position", "pos", "i", "j", "n"}) {
393+
return true
394+
}
395+
return containsAny(loweredTarget, []string{"array", "list", "slice", "items", "rows", "columns", "chars", "parts", "tokens", "segments", "lines", "values"}) &&
396+
!containsAny(loweredKey, []string{"name", "key", "id", "type", "status", "field"})
397+
}
398+
347399
func nearbyBoundsGuard(statements []support.ParsedStatement, idx int, target string) bool {
348400
target = strings.ToLower(strings.TrimSpace(strings.Split(target, ".")[0]))
349401
if target == "" {
@@ -427,17 +479,21 @@ func missingResourceLimitLine(fn precisionFunction, loweredBody string) (int, bo
427479
if boundedReadByteLengthCheck(loweredBody) {
428480
return 0, false
429481
}
482+
if preFormDataContentLengthHelperGuard(loweredBody) {
483+
return 0, false
484+
}
430485
return firstPatternLine(fn, resourceReadPattern), true
431486
}
432487

433488
func resourceLimitProofPattern(loweredBody string) bool {
434489
if containsAny(loweredBody, []string{
435490
"limitreader", "maxbytes", "max_bytes", "content-length", "contentlength",
436-
"limit(", "take(", "slice(", ".slice(", "buffer_size", "quota",
491+
"limit(", "take(", "take:", ".take", "slice(", ".slice(", "buffer_size", "quota",
492+
"maxresults", "max_results", "page_size", "pagesize",
437493
}) {
438494
return true
439495
}
440-
return resourceCountGuard.MatchString(loweredBody) || resourceNamedCountLimit.MatchString(loweredBody)
496+
return resourceCountGuard.MatchString(loweredBody) || resourceNamedCountLimit.MatchString(loweredBody) || prismaTakePattern.MatchString(loweredBody)
441497
}
442498

443499
func uploadValidationHelperPattern(loweredBody string) bool {
@@ -452,6 +508,17 @@ func boundedReadByteLengthCheck(loweredBody string) bool {
452508
return containsAny(loweredBody, []string{"bytelength", "byte_length", ".length > max", ".length > limit", "buffer.length", "bytes.length"})
453509
}
454510

511+
func preFormDataContentLengthHelperGuard(loweredBody string) bool {
512+
if !strings.Contains(loweredBody, "formdata") {
513+
return false
514+
}
515+
return containsAny(loweredBody, []string{
516+
"assertcontentlength", "ensurecontentlength", "validatecontentlength", "guardcontentlength",
517+
"assertrequestsize", "ensurerequestsize", "validaterequestsize", "guardrequestsize",
518+
"assertuploadsize", "ensureuploadsize", "validateuploadsize", "guarduploadsize",
519+
})
520+
}
521+
455522
func invalidStateTransitionLine(fn precisionFunction, loweredBody string) (int, bool) {
456523
if !stateAssignmentPattern.MatchString(functionRawBody(fn)) {
457524
return 0, false

internal/codeguard/checks/quality/quality_errors.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ func cleanupIgnoredLine(statements []support.ParsedStatement) (int, bool) {
187187
}
188188

189189
func partialFailureHiddenLine(fn precisionFunction, loweredBody string) (int, bool) {
190+
if partialFailureSurfacedInResult(loweredBody) {
191+
return 0, false
192+
}
190193
if strings.Contains(loweredBody, "allsettled") && !containsAny(loweredBody, []string{"rejected", "throw", "return err", "return error"}) {
191194
return fn.StartLine, true
192195
}
@@ -205,6 +208,24 @@ func partialFailureHiddenLine(fn precisionFunction, loweredBody string) (int, bo
205208
return 0, false
206209
}
207210

211+
func partialFailureSurfacedInResult(loweredBody string) bool {
212+
if !containsAny(loweredBody, []string{"diagnostic", "diagnostics", "errors", "failures", "warnings"}) {
213+
return false
214+
}
215+
recordsFailure := containsAny(loweredBody, []string{
216+
"diagnostics.push", "errors.push", "failures.push", "warnings.push",
217+
"append(diagnostics", "append(errors", "append(failures", "append(warnings",
218+
"diagnostics = append", "errors = append", "failures = append", "warnings = append",
219+
})
220+
if !recordsFailure {
221+
return false
222+
}
223+
return containsAny(loweredBody, []string{
224+
"return {", "return result", "diagnostics:", "errors:", "failures:", "warnings:",
225+
"diagnostics,", "errors,", "failures,", "warnings,",
226+
})
227+
}
228+
208229
func fallbackHidesCorruptionLine(fn precisionFunction, loweredBody string) (int, bool) {
209230
if !containsAny(loweredBody, []string{"json", "parse", "deserialize", "unmarshal", "decode", "validate", "corrupt"}) {
210231
return 0, false

internal/codeguard/checks/quality/quality_precision.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var (
4545
"misc": {}, "stuff": {}, "value": {}, "values": {},
4646
}
4747
queryFunctionPrefixPattern = regexp.MustCompile(`^(get|find|list|load|read|lookup|fetch|is|has|can|should|compute|calculate|build|format|parse)`)
48-
mutatingCallPattern = regexp.MustCompile(`(?i)(^|[.>:\-_])(add|append|assign|clear|create|delete|emit|insert|mutate|persist|pop|publish|push|push_back|remove|reverse|save|send|set|sort|splice|store|update|upsert|write)([A-Z_:\-.]|$)`)
48+
mutatingCallPattern = regexp.MustCompile(`(?i)(^|[.>:\-_])(add|allocate|append|assign|clear|create|delete|emit|insert|mutate|persist|pop|publish|push|push_back|remove|reverse|save|send|set|sort|splice|store|update|upsert|write)([A-Z_:\-.]|$)`)
4949
lowLevelOperationPattern = regexp.MustCompile(`(?i)(\bsql\.|\.query\(|\.exec\(|\bhttp\.|\bfetch\(|\baxios\.|\brequests\.|\bjson\.|\bJSON\.|\bos\.Getenv\b|\bprocess\.env\b|\bfs\.|#include\b)`)
5050
primitiveTypePattern = regexp.MustCompile(`(?i)\b(string|str|int|int64|float|float64|double|decimal|number|boolean|bool|char|long|short)\b`)
5151
domainPrimitiveNamePattern = regexp.MustCompile(`(?i)(id|status|state|type|kind|currency|amount|price|email|phone|country|role|permission|tenant|account|customer|order)`)

internal/codeguard/checks/quality/quality_precision_workstreams_cd.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
)
3131

3232
var (
33-
commandFunctionPrefixPattern = regexp.MustCompile(`^(add|append|assign|cancel|clear|close|create|delete|disable|emit|enable|insert|mutate|notify|open|persist|publish|record|remove|reset|save|send|set|store|submit|toggle|update|upsert|upload|write)`)
33+
commandFunctionPrefixPattern = regexp.MustCompile(`^(add|allocate|append|assign|cancel|clear|close|create|delete|disable|emit|enable|insert|mutate|notify|open|persist|publish|record|remove|reset|save|send|set|store|submit|toggle|update|upsert|upload|write)`)
3434
readCallPattern = regexp.MustCompile(`(?i)(^|[.>:\-_])(count|fetch|find|get|list|load|lookup|query|read|select|search)([A-Z_:\-.]|$)`)
3535
identifierTokenPattern = regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*`)
3636
infraNamePattern = regexp.MustCompile(`(?i)(sql|http|redis|kafka|grpc|graphql|mongo|s3|dynamo|postgres|mysql|elastic|orm)`)
@@ -330,7 +330,7 @@ func explicitMutationName(name string) bool {
330330
}
331331

332332
func inconsistentReturnContract(fn precisionFunction) bool {
333-
if nextResponseNullableGuardHelper(fn) {
333+
if nextResponseNullableGuardHelper(fn) || nullableParserLookupContract(fn) {
334334
return false
335335
}
336336
returns := returnCategories(fn.Body)
@@ -351,6 +351,21 @@ func nextResponseNullableGuardHelper(fn precisionFunction) bool {
351351
return strings.Contains(body, "return null") && hasNextResponseBody
352352
}
353353

354+
func nullableParserLookupContract(fn precisionFunction) bool {
355+
loweredName := strings.ToLower(strings.Trim(fn.Name, "_$"))
356+
if !regexp.MustCompile(`^(as|exists|extract|find|get|lookup|parse|read|resolve|to)`).MatchString(loweredName) {
357+
return false
358+
}
359+
body := strings.ToLower(fn.Body)
360+
if loweredName == "exists" || strings.HasPrefix(loweredName, "exists") {
361+
return containsAny(body, []string{"return false", "return true"})
362+
}
363+
signature := strings.ToLower(fn.Signature)
364+
hasNullableReturnEvidence := containsAny(signature, []string{"| null", "|null", "null", "undefined", "optional"}) ||
365+
containsAny(body, []string{"return null", "return undefined", "return none", "return nil"})
366+
return hasNullableReturnEvidence && containsAny(body, []string{"return null", "return undefined", "return none", "return nil"})
367+
}
368+
354369
type returnShapeCounts struct {
355370
total int
356371
empty bool
@@ -387,7 +402,7 @@ func returnCategories(body string) returnShapeCounts {
387402
func isEmptyReturnExpr(expr string) bool {
388403
expr = strings.TrimSpace(strings.TrimSuffix(expr, ";"))
389404
switch strings.ToLower(expr) {
390-
case "", "nil", "none", "null", "undefined", "false":
405+
case "", "nil", "none", "null", "undefined":
391406
return true
392407
default:
393408
return strings.HasPrefix(expr, "nil,") || strings.HasPrefix(expr, "none,") || strings.HasPrefix(expr, "null,")

internal/codeguard/rules/catalog_fix_templates_quality_errors_defensive.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var qualityErrorDefensiveFixTemplates = map[string]core.FixTemplate{
1818
"defensive.invalid-state-representable": {Kind: guided, Text: "Replace boolean combinations/raw strings with an enum, tagged union, or state machine that encodes valid states."},
1919
"defensive.null-assumption": {Kind: guided, Text: "Guard nil/null/None/optional values before dereference, or make the boundary type non-nullable."},
2020
"defensive.integer-overflow": {Kind: guided, Text: "Guard count/size arithmetic before multiplication, addition, shifts, or allocation sizing."},
21-
"defensive.sequence-collision-risk": {Kind: guided, Text: "Replace count-plus-one external IDs with database sequences/UUIDs, or wrap allocation in a bounded unique-collision retry."},
21+
"defensive.sequence-collision-risk": {Kind: guided, Text: "Replace count-plus-one external IDs with database sequences, UUIDs, or a transactional allocator. A bounded P2002/unique-collision retry mitigates collisions but should still be treated as architecture debt."},
2222
"defensive.bounds-assumption": {Kind: guided, Text: "Check length/existence before indexing, or use a safe lookup API."},
2323
"defensive.unsafe-default": {Kind: guided, Text: "Make security/safety defaults fail closed and require explicit opt-out for unsafe behavior."},
2424
"defensive.non-exhaustive-branch": {Kind: guided, Text: "Add an explicit default/unreachable branch or exhaustive assertion for enum-like state switches."},

0 commit comments

Comments
 (0)