{"$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":"D7","name":"Architectural Integrity","shortDescription":{"text":"Architectural Integrity"},"helpUri":"https://codehealth.canine.dev/dimensions/D7"},{"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"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"id":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"id":"D16","name":"Bus Factor","shortDescription":{"text":"Bus Factor"},"helpUri":"https://codehealth.canine.dev/dimensions/D16"},{"id":"D20","name":"ADR Quality","shortDescription":{"text":"ADR Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D20"},{"id":"D21","name":"Naming Consistency","shortDescription":{"text":"Naming Consistency"},"helpUri":"https://codehealth.canine.dev/dimensions/D21"},{"id":"D25","name":"ADR Conformance","shortDescription":{"text":"ADR Conformance"},"helpUri":"https://codehealth.canine.dev/dimensions/D25"},{"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"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"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"]}],"properties":{"cwe":["CWE-79","CWE-89","CWE-78","CWE-94","CWE-77"]}},{"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"}]}},"results":[{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): administration/application/src/main/java/com/educational/platform/administration/course/approve/ApproveCourseProposalCommandHandler.java:43-49 | administration/application/src/main/java/com/educational/platform/administration/course/decline/DeclineCourseProposalCommandHandler.java:43-49 \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":"administration/application/src/main/java/com/educational/platform/administration/course/approve/ApproveCourseProposalCommandHandler.java"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"e04e65ffa84bc68613a3234a4d907dc3754789020a3a2bd49507677074a6bca2"}},{"ruleId":"D16","level":"warning","message":{"text":"single-maintainer \u2014 knowledge-concentration (bus factor) risk: single-maintainer \u2014 knowledge-concentration (bus factor) risk (1 author(s) across 250 commit(s) sampled)."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"b4b49d961df742f0e507f4cc5c8094fb077ba0391a27fd015d18320865187719"}},{"ruleId":"D20","level":"warning","message":{"text":"Decision is a boilerplate list with links citing design sources but no context/problem and consequences are couched in the decision\u0027s own wording (\u0027it\u0027s forbidden to create a transaction which spans more than one bounded context\u0027): State the concrete data-sharing problem (e.g. shared inventory, order status) that motivated the bounded-context model, then explain why asynchronous communication is preferable over direct calls and event-driven duplication"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0001-bounded-contexts-communication.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"67c69ce0c57bf8e1a81b6d92751247762bd4722682aa126a05a89b81e1066f80"}},{"ruleId":"D20","level":"warning","message":{"text":"Context and decision are both present but the body is clipped mid-sentence (\u0027We can add custom features to Spring functionality when it\u0027s needed.\u0027) before any consequences/trade-offs or a review link appear: Add a Consequences section covering trade-offs such as event-store persistence cost, transactional publish guarantees, and module-review process"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0002-integration-events-implementation.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"7c675768267e0ed5969f39287ea63e6b4744499dba6e2f479ef328356e418310"}},{"ruleId":"D20","level":"warning","message":{"text":"Context lists only the two alternatives (same model vs separate models) and a decision that is restated as \u0027it will be used\u0027 without explaining why CQRS was chosen over those options: Add a Context section comparing the trade-offs of each alternative (e.g. consistency, data duplication, complexity), then state the explicit rationale for choosing CQRS over them"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0003-cqrs.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"af4d132ee26d9012b7f89b8cc413ca5f479810cc3284683784df77f420266a1e"}},{"ruleId":"D20","level":"warning","message":{"text":"Context lists three solutions but never states the problem being solved (e.g. data inconsistency across requests) and the decision is restated verbatim without consequences: Add a one-line Context stating the problem (e.g. inconsistent domain model state when requests arrive out of order) so the chosen always-valid approach is grounded in motivation, then note Consequences such as increased boilerplate for validation annotations"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0004-validation.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"a850d3cd9810c009ea2364e5492455f24f5f2702934605475fbc198e8cdcfe74"}},{"ruleId":"D20","level":"warning","message":{"text":"Decision is narrow and consequences/trade-offs are absent: only that primary keys must not be used for cross-module/external identifier exchange; no mention of why uuids/good natural keys are preferred over each: Add a Consequences section covering trade-offs such as the cost of ensuring good natural keys exist, how to handle cases where one is unavailable, and whether fallback identifiers (e.g. surrogate keys) will be used"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0005-identifier-between-modules.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"76018fb534308b932c4a6564471d2d8bdae6002370304e1e002dd9bf967d7a6e"}},{"ruleId":"D20","level":"warning","message":{"text":"Context lists two alternatives but the decision is restated as a tautology (\u0027rich domain model solution will be used\u0027) with no rationale or trade-offs: Replace the tautological \u0027will be used\u0027 with an explicit rationale (e.g. encapsulation improves maintainability) and enumerate consequences such as increased coupling vs reduced boilerplate"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0006-rich-domain-model.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"4d5682d1f63431a1cab9114687af41dcfb78e1a5758e91c6a7adf3ac16c68e9e"}},{"ruleId":"D20","level":"warning","message":{"text":"Uninformative title and the decision is restated verbatim as Context: Give the ADR a descriptive title (e.g. \u0027Command handlers may return generated identifiers to support query responses\u0027) and add a Consequences section noting that this breaks CQRS purity but is needed for queries"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0008-result-from-comand-handlers.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"6c71be8d54181c83841466a9ca4d76febf4649767985fed7e5baa8ad110602f9"}},{"ruleId":"D20","level":"warning","message":{"text":"Decision is stated but context and consequences fall below the clip marker; no trade-offs or alternatives are mentioned: Add a Consequences section covering trade-offs such as integration complexity with existing custom types and migration effort"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0010-use-axon-framework.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"59c11ad027c349e9481825b0ca7d741c82d91d470d8a8e52bee3b570559bf127"}},{"ruleId":"D20","level":"warning","message":{"text":"No consequences/trade-offs section describing the cost of adopting Axon (e.g. learning curve, configuration complexity) and benefits such as event sourcing: Add a Consequences section weighing adoption cost against the benefit of event sourcing, plus migration effort for existing Spring-event code"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0011-use-axon-event-publishing-mechanism.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"b9447b0f0e12eff94046775c2cbfc68af18c9bff4804e5aba710ec154379a5b5"}},{"ruleId":"D20","level":"warning","message":{"text":"No consequences/trade-offs are stated (e.g., loss of Spring-specific features like data persistence or security): Add a Consequences section noting that without Spring, the application will lose support for existing Spring-only modules and document how framework integration is verified"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0012-make-application-framework-independent.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"046eb3a73915502e2d95bc7a10fb1bc1d4628a971e1f2cd23a026159727aa55a"}},{"ruleId":"D25","level":"error","message":{"text":"ADR not followed: 1. Communications between bounded contexts.: The decision forbids any cross-bounded-context dependency and explicitly prohibits transactions spanning multiple bounded contexts, but the summary shows no types or namespaces reflecting bounded context boundaries. (docs/architecture-decisions/0001-bounded-contexts-communication.md)"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/architecture-decisions/0001-bounded-contexts-communication.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"a3d66b832092da1fbec2b4828f5e24e56a1f19b25e7e858bb213391c85d3d4b2"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: ApproveCourseProposalCommandHandler.java \u2194 DeclineCourseProposalCommandHandler.java: \u0060administration/application/src/main/java/com/educational/platform/administration/course/approve/ApproveCourseProposalCommandHandler.java\u0060 and \u0060administration/application/src/main/java/com/educational/platform/administration/course/decline/DeclineCourseProposalCommandHandler.java\u0060 change together 57% of the time (8 of the 14 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well) with no explicit dependency \u2014 the edge is real but nothing declares it. Read the pair before acting: if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE \u2014 the registration is the link, and it is meant not to be an import \u2014 and the thing to add is a comment on each side naming the other, not a merge; if they simply belong together, co-locate them; if neither holds, the coupling is hidden and worth breaking."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"administration/application/src/main/java/com/educational/platform/administration/course/approve/ApproveCourseProposalCommandHandler.java"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"2745c3aa4246ddf001e7bcbd7cfdf0cf0542e9086271d7489230b02e30925e58"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: SignInCommandHandler.java \u2194 UserRegistrationCommandHandler.java: \u0060users/application/src/main/java/com/educational/platform/users/login/SignInCommandHandler.java\u0060 and \u0060users/application/src/main/java/com/educational/platform/users/registration/UserRegistrationCommandHandler.java\u0060 change together 50% of the time (5 of the 10 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well) with no explicit dependency \u2014 the edge is real but nothing declares it. Read the pair before acting: if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE \u2014 the registration is the link, and it is meant not to be an import \u2014 and the thing to add is a comment on each side naming the other, not a merge; if they simply belong together, co-locate them; if neither holds, the coupling is hidden and worth breaking."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"users/application/src/main/java/com/educational/platform/users/login/SignInCommandHandler.java"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b546d248968d1d5b7d0d4f042f3160958dbfc2ca6f9ab083d36058d2045d36e"}}],"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-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-77","guid":"332c8ade-6612-9f56-a06b-d8d90b1a8750","name":"Command Injection","shortDescription":{"text":"Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/77.html"},{"id":"CWE-78","guid":"2e31ceaf-c7ae-2e5e-9661-cfb1362789cf","name":"OS Command Injection","shortDescription":{"text":"OS Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/78.html"},{"id":"CWE-79","guid":"fd45580b-e8c4-fc5e-8c2f-aa8fab0b4dbf","name":"Cross-site Scripting (XSS)","shortDescription":{"text":"Cross-site Scripting (XSS)"},"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":"SQL Injection","shortDescription":{"text":"SQL Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/89.html"},{"id":"CWE-94","guid":"75e7f50c-6c2f-dd52-bf40-bf6c52b861fd","name":"Code Injection","shortDescription":{"text":"Code Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/94.html"}]}],"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":0,"secretScannerRunsExcluded":0}}}]}