{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"codehealth","informationUri":"https://codehealth.canine.dev","rules":[{"id":"D1","name":"Cyclomatic Complexity","shortDescription":{"text":"Cyclomatic Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D1"},{"id":"D2","name":"Cognitive Complexity","shortDescription":{"text":"Cognitive Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D2"},{"id":"D3","name":"God Classes","shortDescription":{"text":"God Classes"},"helpUri":"https://codehealth.canine.dev/dimensions/D3"},{"id":"D4","name":"Code Duplication","shortDescription":{"text":"Code Duplication"},"helpUri":"https://codehealth.canine.dev/dimensions/D4"},{"id":"D6","name":"Cohesion (LCOM4)","shortDescription":{"text":"Cohesion (LCOM4)"},"helpUri":"https://codehealth.canine.dev/dimensions/D6"},{"id":"D13","name":"Secret Scanning","shortDescription":{"text":"Secret Scanning"},"helpUri":"https://codehealth.canine.dev/dimensions/D13","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"A07:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"],"owasp":"A07:2025"}},{"id":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"id":"D17","name":"Explicit Debt","shortDescription":{"text":"Explicit Debt"},"helpUri":"https://codehealth.canine.dev/dimensions/D17"},{"id":"D19","name":"Documentation Quality","shortDescription":{"text":"Documentation Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D19"},{"id":"D26","name":"Project Cohesion","shortDescription":{"text":"Project Cohesion"},"helpUri":"https://codehealth.canine.dev/dimensions/D26"},{"id":"D28","name":"Secrets (history)","shortDescription":{"text":"Secrets (history)"},"helpUri":"https://codehealth.canine.dev/dimensions/D28","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"A07:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"],"owasp":"A07:2025"}},{"id":"D29","name":"Static Analysis (SAST)","shortDescription":{"text":"Static Analysis (SAST)"},"helpUri":"https://codehealth.canine.dev/dimensions/D29","relationships":[{"target":{"id":"CWE-79","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-89","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-78","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-94","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-77","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"A05:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-79","CWE-89","CWE-78","CWE-94","CWE-77"],"owasp":"A05:2025"}},{"id":"D34","name":"Knowledge Freshness","shortDescription":{"text":"Knowledge Freshness"},"helpUri":"https://codehealth.canine.dev/dimensions/D34"},{"id":"D35","name":"Change Coupling","shortDescription":{"text":"Change Coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/D35"},{"id":"D36","name":"Supply-chain Provenance \u0026 Signing","shortDescription":{"text":"Supply-chain Provenance \u0026 Signing"},"helpUri":"https://codehealth.canine.dev/dimensions/D36","relationships":[{"target":{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-494","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1357","CWE-494"],"owasp":"A03:2025"}},{"id":"D44","name":"Platform End-of-Life","shortDescription":{"text":"Platform End-of-Life"},"helpUri":"https://codehealth.canine.dev/dimensions/D44"},{"id":"AX10","name":"Code composition","shortDescription":{"text":"Code composition"},"helpUri":"https://codehealth.canine.dev/dimensions/AX10"},{"id":"AX3","name":"Project dependency cycles","shortDescription":{"text":"Project dependency cycles"},"helpUri":"https://codehealth.canine.dev/dimensions/AX3"},{"id":"AX4","name":"Dependency direction","shortDescription":{"text":"Dependency direction"},"helpUri":"https://codehealth.canine.dev/dimensions/AX4"},{"id":"AXB1","name":"Runtime evidence locked \u2014 no reproducible boot","shortDescription":{"text":"Runtime evidence locked \u2014 no reproducible boot"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB1"},{"id":"AXB2","name":"Runtime readiness","shortDescription":{"text":"Runtime readiness"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB2"},{"id":"ED1","name":"Handler temporal coupling","shortDescription":{"text":"Handler temporal coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/ED1"},{"id":"ED5","name":"Idempotency","shortDescription":{"text":"Idempotency"},"helpUri":"https://codehealth.canine.dev/dimensions/ED5"},{"id":"M1","name":"Documentation (README)","shortDescription":{"text":"Documentation (README)"},"helpUri":"https://codehealth.canine.dev/dimensions/M1"},{"id":"M2","name":"Architecture documentation","shortDescription":{"text":"Architecture documentation"},"helpUri":"https://codehealth.canine.dev/dimensions/M2"},{"id":"M3","name":"Folder \u0026 project structure","shortDescription":{"text":"Folder \u0026 project structure"},"helpUri":"https://codehealth.canine.dev/dimensions/M3"},{"id":"P1","name":"CI/CD gates","shortDescription":{"text":"CI/CD gates"},"helpUri":"https://codehealth.canine.dev/dimensions/P1"},{"id":"P10","name":"Library API \u0026 versioning","shortDescription":{"text":"Library API \u0026 versioning"},"helpUri":"https://codehealth.canine.dev/dimensions/P10"},{"id":"P12","name":"CI test-gate honesty","shortDescription":{"text":"CI test-gate honesty"},"helpUri":"https://codehealth.canine.dev/dimensions/P12"},{"id":"P3","name":"Security \u0026 performance tooling","shortDescription":{"text":"Security \u0026 performance tooling"},"helpUri":"https://codehealth.canine.dev/dimensions/P3"},{"id":"P4","name":"Deployment \u0026 Rollback","shortDescription":{"text":"Deployment \u0026 Rollback"},"helpUri":"https://codehealth.canine.dev/dimensions/P4"},{"id":"P6","name":"Release Hygiene","shortDescription":{"text":"Release Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/P6"},{"id":"X10","name":"Duplicated predicate","shortDescription":{"text":"Duplicated predicate"},"helpUri":"https://codehealth.canine.dev/dimensions/X10"},{"id":"X20","name":"Mistyped argument guard","shortDescription":{"text":"Mistyped argument guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X20"},{"id":"X21","name":"Side-effecting pattern guard","shortDescription":{"text":"Side-effecting pattern guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X21"},{"id":"X22","name":"Contradicted release guard","shortDescription":{"text":"Contradicted release guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X22"},{"id":"X25","name":"Inert configuration knob","shortDescription":{"text":"Inert configuration knob"},"helpUri":"https://codehealth.canine.dev/dimensions/X25"},{"id":"X26","name":"Unsynchronised callback handoff","shortDescription":{"text":"Unsynchronised callback handoff"},"helpUri":"https://codehealth.canine.dev/dimensions/X26"},{"id":"X27","name":"Collection changed while being enumerated","shortDescription":{"text":"Collection changed while being enumerated"},"helpUri":"https://codehealth.canine.dev/dimensions/X27"},{"id":"X28","name":"Index access outside its own emptiness guard","shortDescription":{"text":"Index access outside its own emptiness guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X28"},{"id":"X3","name":"Exception handling","shortDescription":{"text":"Exception handling"},"helpUri":"https://codehealth.canine.dev/dimensions/X3"},{"id":"X31","name":"Test-only surface in a production module","shortDescription":{"text":"Test-only surface in a production module"},"helpUri":"https://codehealth.canine.dev/dimensions/X31"},{"id":"X32","name":"Type resolved by simple name across every loaded assembly","shortDescription":{"text":"Type resolved by simple name across every loaded assembly"},"helpUri":"https://codehealth.canine.dev/dimensions/X32"},{"id":"X5","name":"Nullable reference types","shortDescription":{"text":"Nullable reference types"},"helpUri":"https://codehealth.canine.dev/dimensions/X5"},{"id":"X6","name":"Hand-rolled structured-format parsing","shortDescription":{"text":"Hand-rolled structured-format parsing"},"helpUri":"https://codehealth.canine.dev/dimensions/X6"},{"id":"X9","name":"Subsumed condition operand","shortDescription":{"text":"Subsumed condition operand"},"helpUri":"https://codehealth.canine.dev/dimensions/X9"}]}},"results":[{"ruleId":"D1","level":"warning","message":{"text":"ocibuild_progress.manager_loop (cyclomatic 19): ocibuild_progress.manager_loop has cyclomatic complexity 19 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/util/ocibuild_progress.erl"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"fe4f3d6dfd0f84d395d9d80ebb0f6a98cf317b60cff11f43b79c088a02f03762"}},{"ruleId":"D1","level":"warning","message":{"text":"ocibuild_release.push_tarball_impl (cyclomatic 18): ocibuild_release.push_tarball_impl has cyclomatic complexity 18 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":1390}}}],"partialFingerprints":{"codehealthFindingId/v1":"50f54222dc25a94c28a28fce2096bd6085d3ea14eef1d337dbf5b9b3b72cc956"}},{"ruleId":"D2","level":"warning","message":{"text":"ocibuild_release.app_dependency_names (cognitive 21): ocibuild_release.app_dependency_names has cognitive complexity 21 (threshold 15). Drivers by points: if/else 2 (8 pts), match/switch 4 (7 pts), loops 2 (6 pts) (nesting depth added 13). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":3201}}}],"partialFingerprints":{"codehealthFindingId/v1":"46ec63e236e0a7ece00d866761a89ab98683efd89a325cf41abb1d32f08efd5c"}},{"ruleId":"D2","level":"warning","message":{"text":"ocibuild_registry.pull_manifest (cognitive 18): ocibuild_registry.pull_manifest has cognitive complexity 18 (threshold 15). Drivers by points: match/switch 6 (18 pts) (nesting depth added 12). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":147}}}],"partialFingerprints":{"codehealthFindingId/v1":"63db65f357f381d0b16f911cada9717f71204420edd037d1ed65ac8b5ba649b7"}},{"ruleId":"D2","level":"warning","message":{"text":"ocibuild_release.do_build_image (cognitive 17): ocibuild_release.do_build_image has cognitive complexity 17 (threshold 15). Drivers by points: match/switch 7 (14 pts), error handling 3 (nesting depth added 7). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":1049}}}],"partialFingerprints":{"codehealthFindingId/v1":"51417580dcc0b862f8ebd2b4cbaa6d4e1aec733b186f5602c420f2f2f696df4e"}},{"ruleId":"D2","level":"warning","message":{"text":"ocibuild_registry.exchange_token (cognitive 16): ocibuild_registry.exchange_token has cognitive complexity 16 (threshold 15). Drivers by points: match/switch 8 (16 pts) (nesting depth added 8). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1538}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d9f6a38869ab93b8d455c91003c8bcb0447f0add951d31c932c8bfc5e7666d2"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: http/ocibuild_registry.erl: FileTooLong \u2014 2173 significant lines (blank, comment-only and punctuation-only lines excluded), declaring 114 functions. The bar is 500 significant lines; this is 1673 over it, 4.35\u00D7 the bar. In this language a module is exactly one source file, so its length cannot be moved into sibling files of the same module. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no one module has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"a91697343e221103230b4b3023dbd785badca58977288495c18e271ffd50a6aa"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: adapters/ocibuild_release.erl: FileTooLong \u2014 2133 significant lines (blank, comment-only and punctuation-only lines excluded), declaring 123 functions. The bar is 500 significant lines; this is 1633 over it, 4.27\u00D7 the bar. In this language a module is exactly one source file, so its length cannot be moved into sibling files of the same module. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no one module has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"0e1fa17373e913c38e94eeb9060fc15596261b73d31c2290dd3dc93c1568d707"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: oci/ocibuild_layout.erl: FileTooLong \u2014 721 significant lines (blank, comment-only and punctuation-only lines excluded), declaring 57 functions. The bar is 500 significant lines; this is 221 over it, 1.44\u00D7 the bar. In this language a module is exactly one source file, so its length cannot be moved into sibling files of the same module. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no one module has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_layout.erl"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"4d0c89cfd1a249751ffdb378ffdef78984778f70d2e5f5643fe5d80428577064"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFunctions: ocibuild_rebar3: TooManyFunctions \u2014 42 functions. The bar is 30 functions; this is 12 over it, 1.40\u00D7 the bar. The counted members are a module\u0027s functions \u2014 a module holds no instance state, so there is no shared data to group them by and no type to move them onto. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no single module carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_rebar3.erl"},"region":{"startLine":2}}}],"partialFingerprints":{"codehealthFindingId/v1":"3231f73a07a8b006094b36db7faaede0ab4f5d7f84ee4bb45f1d971a8f9af78c"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFunctions: ocibuild: TooManyFunctions \u2014 38 functions. The bar is 30 functions; this is 8 over it, 1.27\u00D7 the bar. The counted members are a module\u0027s functions \u2014 a module holds no instance state, so there is no shared data to group them by and no type to move them onto. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no single module carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/ocibuild.erl"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"117dad5a54be1939b754a822ac34a82758bd16d0a8ed96deb290212c007acd57"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFunctions: ocibuild_sbom: TooManyFunctions \u2014 31 functions. The bar is 30 functions; this is 1 over it, 1.03\u00D7 the bar. The counted members are a module\u0027s functions \u2014 a module holds no instance state, so there is no shared data to group them by and no type to move them onto. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no single module carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_sbom.erl"},"region":{"startLine":2}}}],"partialFingerprints":{"codehealthFindingId/v1":"56ef51fa20df26652d3ad94c8dcc1de067a0d372f9557584982aaacf0448ebe5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (41\u201342 lines \u00D7 2): src/http/ocibuild_registry.erl:503-543 | src/http/ocibuild_registry.erl:2233-2274 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":503}}}],"partialFingerprints":{"codehealthFindingId/v1":"13fc358df7f78368e9d8a647e747615dbf74e080da1254e5c8301ef0b508e26f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17\u201318 lines \u00D7 2): src/adapters/ocibuild_release.erl:892-908 | src/adapters/ocibuild_release.erl:1477-1494 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":892}}}],"partialFingerprints":{"codehealthFindingId/v1":"0c8969c1eeaf724bc877a6b7835697eb369833b556821e82c32d4b685d972a36"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14\u201315 lines \u00D7 2): src/oci/ocibuild_sign.erl:193-207 | src/oci/ocibuild_sign.erl:251-264 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_sign.erl"},"region":{"startLine":193}}}],"partialFingerprints":{"codehealthFindingId/v1":"fd977207cec246c9ee33a92c97c5c35eaf66dc78f9a8c84e623353dd8406184c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9\u201312 lines \u00D7 6): src/http/ocibuild_registry.erl:549-558 | src/http/ocibuild_registry.erl:786-794 | src/http/ocibuild_registry.erl:855-864 | src/http/ocibuild_registry.erl:985-994 | src/http/ocibuild_registry.erl:1118-1127 | src/http/ocibuild_registry.erl:2273-2284 \u2014 all 6 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":549}}}],"partialFingerprints":{"codehealthFindingId/v1":"a01798804b6cee6ead4bc1bb7d13e46540acbf2996edd39a1a28bda4b7645004"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201312 lines \u00D7 2): src/http/ocibuild_registry.erl:314-325 | src/http/ocibuild_registry.erl:356-366 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":314}}}],"partialFingerprints":{"codehealthFindingId/v1":"25302c5251c4e2c0c74c272e1aaa31e3bb58fb6d58f69bf65a50015c7feb6c1c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): src/http/ocibuild_registry.erl:928-939 | src/http/ocibuild_registry.erl:1063-1074 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":928}}}],"partialFingerprints":{"codehealthFindingId/v1":"1d48b169d1d78f9650bb9aab95c7cc9cd255e6364939b900e9b0c0fb5d9966c6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): src/http/ocibuild_registry.erl:1840-1851 | src/http/ocibuild_registry.erl:1863-1874 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1840}}}],"partialFingerprints":{"codehealthFindingId/v1":"92fa86dfec93b6bbf2ec83a4eb4f027b8f9f9aa585d53afd6e4c7acb073766a2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8\u201312 lines \u00D7 2): src/http/ocibuild_registry.erl:1985-1996 | src/http/ocibuild_registry.erl:2041-2048 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1985}}}],"partialFingerprints":{"codehealthFindingId/v1":"dca62eebebda5911877bed2ff4125cb4b22788fccacc55e0b72881ff05bf9ac0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9\u201311 lines \u00D7 2): src/http/ocibuild_registry.erl:2828-2836 | src/http/ocibuild_registry.erl:2838-2848 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2828}}}],"partialFingerprints":{"codehealthFindingId/v1":"68a2e016e270f80c4833b40fbc63200f38a01ee9ee00a17412a01444ae66a44e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): src/oci/ocibuild_layout.erl:98-108 | src/oci/ocibuild_layout.erl:176-186 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_layout.erl"},"region":{"startLine":98}}}],"partialFingerprints":{"codehealthFindingId/v1":"acf7f578d7481fbaddcb767b5116bf4eb01bafa47e47c152da6f126a079dba7a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): src/adapters/ocibuild_release.erl:347-356 | src/adapters/ocibuild_release.erl:368-377 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":347}}}],"partialFingerprints":{"codehealthFindingId/v1":"1a4483fc84ce5fe4abf7436a0e25174c27f90f10495127d4d55afdcf114461f9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): src/http/ocibuild_cache.erl:240-249 | src/oci/ocibuild_layout.erl:472-481 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_cache.erl"},"region":{"startLine":240}}}],"partialFingerprints":{"codehealthFindingId/v1":"2230cecb8dc95c348fa301121608bb1ca13ab89e21bad48ae027dd2ea9a0e609"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9\u201310 lines \u00D7 2): src/http/ocibuild_registry.erl:362-371 | src/http/ocibuild_registry.erl:873-881 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":362}}}],"partialFingerprints":{"codehealthFindingId/v1":"87e926bbc368857eb38c6f88235b85203ed565f288fba0ea3fdaf4a52339ceca"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): src/http/ocibuild_registry.erl:485-494 | src/http/ocibuild_registry.erl:795-804 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":485}}}],"partialFingerprints":{"codehealthFindingId/v1":"eb5f0ca2ea4ddee90873fcc15f7c75747b76c691d7663e5f84e2f3079eb64d5d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): src/oci/ocibuild_layout.erl:94-105 | src/oci/ocibuild_layout.erl:175-183 | src/oci/ocibuild_layout.erl:716-728 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_layout.erl"},"region":{"startLine":94}}}],"partialFingerprints":{"codehealthFindingId/v1":"909b9955ccebcc2a529751ce2fa2c69a41d31b6c09d6ba22243dd5505a28695a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20139 lines \u00D7 2): src/http/ocibuild_registry.erl:873-878 | src/http/ocibuild_registry.erl:1927-1935 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":873}}}],"partialFingerprints":{"codehealthFindingId/v1":"4e7d5b01d0e60e1bed6265e7d366663db72030b829cc39ae39409e7814d76b02"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): src/adapters/ocibuild_release.erl:2314-2320 | src/util/ocibuild_progress.erl:408-414 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":2314}}}],"partialFingerprints":{"codehealthFindingId/v1":"8e02ab1acdc7639b5b0ba848488e24dde2e6ffb7c5937afcff7e226a10776f51"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): src/http/ocibuild_registry.erl:1903-1909 | src/oci/ocibuild_layout.erl:911-917 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1903}}}],"partialFingerprints":{"codehealthFindingId/v1":"fda2eb29134c78e41021d4a5ac433516bd04b9f7ebb29aa655752f79359b9e40"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (4\u20137 lines \u00D7 3): src/http/ocibuild_registry.erl:2455-2458 | src/http/ocibuild_registry.erl:2480-2485 | src/http/ocibuild_registry.erl:2753-2759 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2455}}}],"partialFingerprints":{"codehealthFindingId/v1":"29ad46931193122024ac07e612492e748f68938f4f189a55241e02d0e8ee2048"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): src/http/ocibuild_registry.erl:2541-2546 | src/http/ocibuild_registry.erl:2582-2587 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2541}}}],"partialFingerprints":{"codehealthFindingId/v1":"ddc1fa6692fc7703e66f0b0cad7c997bc21e69568ef7ebeb8a32571b73020f5a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): src/http/ocibuild_registry.erl:2559-2564 | src/http/ocibuild_registry.erl:2602-2607 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2559}}}],"partialFingerprints":{"codehealthFindingId/v1":"baee1008a6ba5931920185a86353c74f77c5688fb7fad5c2ae3c21b3a72d7e3d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): src/oci/ocibuild_tar.erl:230-235 | src/oci/ocibuild_tar.erl:259-265 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_tar.erl"},"region":{"startLine":230}}}],"partialFingerprints":{"codehealthFindingId/v1":"25ee9dd88c5ffdc6c5f721ff3e854cfac7832db6e276c942e5ebcbb8f3ebbb94"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): src/adapters/ocibuild_release.erl:3211-3215 | src/adapters/ocibuild_release.erl:3251-3255 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/adapters/ocibuild_release.erl"},"region":{"startLine":3211}}}],"partialFingerprints":{"codehealthFindingId/v1":"2bb6127cb261cde86457179be70de04568a5f50970e9c2584a7a7a4b8bd1a5a6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 7): src/http/ocibuild_registry.erl:549-557 | src/http/ocibuild_registry.erl:578-586 | src/http/ocibuild_registry.erl:786-793 | src/http/ocibuild_registry.erl:855-863 | src/http/ocibuild_registry.erl:985-993 | src/http/ocibuild_registry.erl:1118-1126 | src/http/ocibuild_registry.erl:2273-2283 \u2014 all 7 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":549}}}],"partialFingerprints":{"codehealthFindingId/v1":"b74f3e79d0c80c5820c8fe30890857047f064c9a34362a691cf799be6a955dd5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): src/http/ocibuild_registry.erl:2574-2579 | src/http/ocibuild_registry.erl:2594-2599 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2574}}}],"partialFingerprints":{"codehealthFindingId/v1":"857f85fd7969f0ee7b69cc79428e059f3356d01451e1687475d606cc750f374b"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"1b213f6eedd4b140d0bc37bdf1496f72811a643f34064f12518b32e9e83bcfc7"},"taxa":[{"id":"CICD-SEC-9","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"D36","level":"warning","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"9f1b533d3dd28ac610f5e0b60b5bf0199a87b9f9cc704b8c98358380d04c7902"},"taxa":[{"id":"CICD-SEC-4","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ace515990e7ee0f17b5c17e44dd168a5bdecf90804a3a3dd845cf05540978013"},"taxa":[{"id":"CICD-SEC-3","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"ED1","level":"warning","message":{"text":"Handler temporal coupling: ocibuild_registry clause \u0060handle_www_authenticate/4#1423\u0060: \u0060ocibuild_registry\u0060 clause \u0060handle_www_authenticate/4#1423\u0060 blocks the process on a synchronous outbound I/O client call (a remote HTTP request, a socket receive, or a blocking pool checkout) awaited inline while handling a message \u2014 the process is blocked (holding its mailbox) until a peer replies or the sleep elapses. That couples this handler in real time to another process being up and fast, and a mutual call can DEADLOCK. To decouple, issue the request off the hot path \u2014 reply asynchronously and do the I/O in a throwaway spawned worker (or use the async client API), so the process keeps serving its mailbox."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2460}}}],"partialFingerprints":{"codehealthFindingId/v1":"074d03b9deff5d9fe448face826b8640a49909f55f6408e28fe138c16c3505c1"}},{"ruleId":"M2","level":"note","message":{"text":"No ADRs: No Architecture Decision Records found \u2014 no conventional ADR directory, no numbered \u0060NNNN-title\u0060 documents in any markup this check reads, and nothing ADR-shaped by content. Design rationale recorded elsewhere (a design-notes tree, a mailing list, pull-request discussion) is not visible to this check and is not re-findable per decision, so a future maintainer cannot ask why one choice was made and get an answer."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"670b3d6e36a756d63097d0dfbf90afd5fc761308800b9354894a07c3f4e4aa14"}},{"ruleId":"P3","level":"note","message":{"text":"No SAST: No static application security testing detected. For this repository\u0027s stack, add dialyzer or elvis as a CI step. What was searched, so you can tell an absence from a miss: the 12920 CI workflow file(s) in this repository, and the scanner and linter configuration checked in beside them. A scan that runs outside CI, one configured in your forge\u0027s web UI rather than in a committed file, or a tool whose name is none of those this check carries, is not seen \u2014 if that is your case the row is wrong, and saying so is more useful than adding a second scanner."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":".github/workflows/ci.yaml"},"region":{"startLine":19}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e54424179c892f03ef2fd003130ac4bd43f39bbf3b1ca0a0143e25acb80ec87"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060is_integer(size) and size \u003E= 1 and size \u003C= 100\u0060 appears character-identically in 2 files \u2014 lib/mix/tasks/ocibuild.ex, lib/ocibuild/mix_release.ex. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"lib/mix/tasks/ocibuild.ex"},"region":{"startLine":257}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b9a6de6658acb4c64da8abde8e9804fbab759f12de741bdd7e10480766d716a"}},{"ruleId":"X3","level":"warning","message":{"text":"Swallowed exception (caught, then discarded): \u0060catch _:_\u0060 takes every exception and records none of it \u2014 the body neither logs it, rethrows it, nor even names it, so the failure is discarded as completely as by an empty catch and only the substituted value survives. Log it through whatever this codebase already uses to report problems, narrow the catch to the exception this call can actually raise, or say in a comment on the catch why the failure genuinely cannot matter."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/vcs/ocibuild_vcs_git.erl"},"region":{"startLine":221}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e3b6e259e022c91acd0cbe45c9887d42b320b1198421a9dc5fb520d52d12fff"}},{"ruleId":"X3","level":"warning","message":{"text":"Swallowed exception (caught, then discarded): \u0060catch error:_\u0060 takes every exception and records none of it \u2014 the body neither logs it, rethrows it, nor even names it, so the failure is discarded as completely as by an empty catch and only the substituted value survives. Log it through whatever this codebase already uses to report problems, narrow the catch to the exception this call can actually raise, or say in a comment on the catch why the failure genuinely cannot matter."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/util/ocibuild_time.erl"},"region":{"startLine":102}}}],"partialFingerprints":{"codehealthFindingId/v1":"3948e1cfa094a8405926fdd1dd703146c31ed336661d793822aab51efbff4c77"}},{"ruleId":"X3","level":"warning","message":{"text":"Swallowed exception (caught, then discarded): \u0060catch _:_\u0060 takes every exception and records none of it \u2014 the body neither logs it, rethrows it, nor even names it, so the failure is discarded as completely as by an empty catch and only the substituted value survives. Log it through whatever this codebase already uses to report problems, narrow the catch to the exception this call can actually raise, or say in a comment on the catch why the failure genuinely cannot matter."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/oci/ocibuild_sbom.erl"},"region":{"startLine":634}}}],"partialFingerprints":{"codehealthFindingId/v1":"404f3c4384126c99568a49d15e5ba230b7d43febbc4e3f0967584df1b0f7b2a3"}},{"ruleId":"X3","level":"warning","message":{"text":"Swallowed exception (caught, then discarded): \u0060catch _:_\u0060 takes every exception and records none of it \u2014 the body neither logs it, rethrows it, nor even names it, so the failure is discarded as completely as by an empty catch and only the substituted value survives. Log it through whatever this codebase already uses to report problems, narrow the catch to the exception this call can actually raise, or say in a comment on the catch why the failure genuinely cannot matter."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":1531}}}],"partialFingerprints":{"codehealthFindingId/v1":"effa59a62f133fce899feea39ee803148428ae4916806915dfbe66fbd62ce8f3"}},{"ruleId":"X3","level":"warning","message":{"text":"Swallowed exception (caught, then discarded): \u0060catch _:_\u0060 takes every exception and records none of it \u2014 the body neither logs it, rethrows it, nor even names it, so the failure is discarded as completely as by an empty catch and only the substituted value survives. Log it through whatever this codebase already uses to report problems, narrow the catch to the exception this call can actually raise, or say in a comment on the catch why the failure genuinely cannot matter."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":2887}}}],"partialFingerprints":{"codehealthFindingId/v1":"d12fdaca892fd5a86c095c58d5ae395e0b6bfaf32f9360bf7e1d1eeade686905"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_get/2\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"cb12d8e95d8b8df2abcf5cc0431a289642fc6bf27b303b0cb878243bb82da4ff"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_head/2\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"234ec596956b3560264e31177d38c832e74707725dc9bda69026582d14f23617"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_post/3\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"3ba0aaf570abe4fbec923e808303b19f09f41bdedae8fab64244ee883ba40434"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_patch/4\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"8f4c7ea6229cd972767cb436d8d7e7d1ac3463a2357c87185f3467793d3bf067"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_put/3\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"f2d4ef8bfa29c73d416b0b1c96fe9e7bbe6c64f82cabe109427e15ca58a6ddb5"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:http_put/4\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"0c45443fb2c0215b5b7ae0b010f6fafdaa07e9e52e37af4448d2b6030b375b7b"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:push_blob/5\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"58cda426da331cc938a97bcaf79d23b6dd1a0d353d1ee2009a3c624156d62152"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:push_blob/6\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"3c8d47bbe654d10a9db2e3299cee9ef60001b61fed49d92e878ccf01bd43dc16"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:format_content_range/2\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"97cceefba26e58f0ed283ee6b94ce042b12d4ee09f8441cd1e0d5e99318e2393"}},{"ruleId":"X31","level":"note","message":{"text":"Test-only surface in a production module: \u0060ocibuild_registry:parse_range_header/1\u0060 is exported unconditionally, and the comment directly above the \u0060-export\u0060 on this line says the exports below it are for testing. Erlang has no visibility below \u0060-export\u0060, so this is part of the module\u0027s public contract: it cannot be changed without a compatibility argument. This arm read the comment, not the call graph: if the comment is right and only tests call it, wrapping the attribute in \u0060-ifdef(TEST). \u2026 -endif.\u0060 keeps the test\u0027s access and removes it from the shipped module. If production code calls it, the comment is stale and wrapping the export would break the build \u2014 delete the comment and document the function as part of the module\u0027s API."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/http/ocibuild_registry.erl"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"63fc13a182fc2f87b5a666adb80852d724261ba5c34cc7f10df8a86f152aec16"}}],"taxonomies":[{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d","organization":"MITRE","informationUri":"https://cwe.mitre.org/","isComprehensive":false,"shortDescription":{"text":"The MITRE Common Weakness Enumeration (CWE)."},"taxa":[{"id":"CWE-1357","guid":"e4d2e772-757e-0a5c-bd7d-77052949d866","name":"Reliance on Insufficiently Trustworthy Component","shortDescription":{"text":"Reliance on Insufficiently Trustworthy Component"},"helpUri":"https://cwe.mitre.org/data/definitions/1357.html"},{"id":"CWE-259","guid":"ae9ad959-fbb6-9d5e-892d-3dca66da0b69","name":"Use of Hard-coded Password","shortDescription":{"text":"Use of Hard-coded Password"},"helpUri":"https://cwe.mitre.org/data/definitions/259.html"},{"id":"CWE-494","guid":"b8a65e0d-e459-4a55-a931-fc1136482375","name":"Download of Code Without Integrity Check","shortDescription":{"text":"Download of Code Without Integrity Check"},"helpUri":"https://cwe.mitre.org/data/definitions/494.html"},{"id":"CWE-77","guid":"332c8ade-6612-9f56-a06b-d8d90b1a8750","name":"Improper Neutralization of Special Elements used in a Command (\u0027Command Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in a Command (\u0027Command Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/77.html"},{"id":"CWE-78","guid":"2e31ceaf-c7ae-2e5e-9661-cfb1362789cf","name":"Improper Neutralization of Special Elements used in an OS Command (\u0027OS Command Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in an OS Command (\u0027OS Command Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/78.html"},{"id":"CWE-79","guid":"fd45580b-e8c4-fc5e-8c2f-aa8fab0b4dbf","name":"Improper Neutralization of Input During Web Page Generation (\u0027Cross-site Scripting\u0027)","shortDescription":{"text":"Improper Neutralization of Input During Web Page Generation (\u0027Cross-site Scripting\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/79.html"},{"id":"CWE-798","guid":"5e8f057d-fee3-995a-a0cb-9fc5b0d174d1","name":"Use of Hard-coded Credentials","shortDescription":{"text":"Use of Hard-coded Credentials"},"helpUri":"https://cwe.mitre.org/data/definitions/798.html"},{"id":"CWE-89","guid":"6d08fdad-37eb-c150-bbf0-d7d946863407","name":"Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/89.html"},{"id":"CWE-94","guid":"75e7f50c-6c2f-dd52-bf40-bf6c52b861fd","name":"Improper Control of Generation of Code (\u0027Code Injection\u0027)","shortDescription":{"text":"Improper Control of Generation of Code (\u0027Code Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/94.html"}]},{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61","version":"2025","organization":"OWASP Foundation","informationUri":"https://top10.owasp.org/2025/","isComprehensive":false,"shortDescription":{"text":"The OWASP Top 10:2025 web application security risks."},"taxa":[{"id":"A03:2025","guid":"168e227c-7735-2e57-bc6e-997affe644c5","name":"Software Supply Chain Failures","shortDescription":{"text":"Software Supply Chain Failures"},"helpUri":"https://top10.owasp.org/2025/A03_2025-Software_Supply_Chain_Failures/"},{"id":"A05:2025","guid":"87e86033-3231-8458-8e4a-5cb82743553a","name":"Injection","shortDescription":{"text":"Injection"},"helpUri":"https://top10.owasp.org/2025/A05_2025-Injection/"},{"id":"A07:2025","guid":"334ddd78-539d-d35c-83dd-253647eb7eaf","name":"Authentication Failures","shortDescription":{"text":"Authentication Failures"},"helpUri":"https://top10.owasp.org/2025/A07_2025-Authentication_Failures/"}]},{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35","organization":"OWASP Foundation","informationUri":"https://owasp.org/www-project-top-10-ci-cd-security-risks/","isComprehensive":false,"shortDescription":{"text":"The OWASP Top 10 CI/CD Security Risks."},"taxa":[{"id":"CICD-SEC-3","guid":"458c83a6-ebf7-cf55-8cc3-b4d949bbf271","name":"Dependency Chain Abuse","shortDescription":{"text":"Dependency Chain Abuse"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-03-Dependency-Chain-Abuse"},{"id":"CICD-SEC-4","guid":"c859d8ea-8b24-a157-9406-94929c10397a","name":"Poisoned Pipeline Execution (PPE)","shortDescription":{"text":"Poisoned Pipeline Execution (PPE)"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-04-Poisoned-Pipeline-Execution"},{"id":"CICD-SEC-9","guid":"b3d36727-0395-5b51-94c4-5e173d62eab3","name":"Improper Artifact Integrity Validation","shortDescription":{"text":"Improper Artifact Integrity Validation"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-09-Improper-Artifact-Integrity-Validation"}]}],"properties":{"codehealthPublication":{"public":true,"notice":"This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings \u2014 which rule fired, in which file, on which line, and how to fix it \u2014 are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.","securityFindingsRedacted":3,"secretScannerRunsExcluded":0}},"redactionTokens":["A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."]}]}