From 1e2b74d79cb517ad2332662a98075a87ae69c41a Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 15 Jul 2026 11:47:47 +0200 Subject: [PATCH] feat: add CHECKMARX_JSON material type Add support for ingesting Checkmarx One native JSON reports (ast-cli ScanResultsCollection produced by `cx --report-format json`) as a new CHECKMARX_JSON material type. The native format carries triage state, similarityId and query metadata that SARIF drops. The crafter validates the report structure (top-level scanID and results, plus per-result type and data) and injects the checkmarx tool annotation. The kind is omitted from auto-detection and must be referenced with an explicit --kind CHECKMARX_JSON. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez Chainloop-Trace-Sessions: 23990771-b817-46fa-8543-353be0e9155a Signed-off-by: Javier Rodriguez --- app/cli/documentation/cli-reference.mdx | 4 +- .../workflowcontract/v1/crafting_schema.ts | 10 + ...on.v1.Attestation.Material.jsonschema.json | 6 +- ...tation.v1.Attestation.Material.schema.json | 6 +- ...tation.v1.PolicyEvaluation.jsonschema.json | 3 +- ...ttestation.v1.PolicyEvaluation.schema.json | 3 +- ...v1.CraftingSchema.Material.jsonschema.json | 3 +- ...act.v1.CraftingSchema.Material.schema.json | 3 +- ...ct.v1.PolicyGroup.Material.jsonschema.json | 3 +- ...ntract.v1.PolicyGroup.Material.schema.json | 3 +- ...flowcontract.v1.PolicySpec.jsonschema.json | 3 +- ...workflowcontract.v1.PolicySpec.schema.json | 3 +- ...owcontract.v1.PolicySpecV2.jsonschema.json | 3 +- ...rkflowcontract.v1.PolicySpecV2.schema.json | 3 +- .../workflowcontract/v1/crafting_schema.pb.go | 14 +- .../workflowcontract/v1/crafting_schema.proto | 3 + .../v1/crafting_schema_validations.go | 4 + .../crafter/materials/checkmarx.go | 129 ++++++++ .../crafter/materials/checkmarx_test.go | 154 +++++++++ .../crafter/materials/materials.go | 2 + .../materials/testdata/checkmarx-clean.json | 5 + .../testdata/checkmarx-null-results.json | 5 + .../crafter/materials/testdata/checkmarx.json | 299 ++++++++++++++++++ 23 files changed, 651 insertions(+), 20 deletions(-) create mode 100644 pkg/attestation/crafter/materials/checkmarx.go create mode 100644 pkg/attestation/crafter/materials/checkmarx_test.go create mode 100644 pkg/attestation/crafter/materials/testdata/checkmarx-clean.json create mode 100644 pkg/attestation/crafter/materials/testdata/checkmarx-null-results.json create mode 100644 pkg/attestation/crafter/materials/testdata/checkmarx.json diff --git a/app/cli/documentation/cli-reference.mdx b/app/cli/documentation/cli-reference.mdx index 1c996dfc9..b0eb5cf65 100755 --- a/app/cli/documentation/cli-reference.mdx +++ b/app/cli/documentation/cli-reference.mdx @@ -262,7 +262,7 @@ Options --annotation strings additional annotation in the format of key=value --attestation-id string Unique identifier of the in-progress attestation -h, --help help for add ---kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --max-extract-entries int max number of files to extract when --value is an archive (default 10000) --max-extract-size string max total uncompressed size to extract when --value is an archive (default "1GiB") --name string name of the material as shown in the contract @@ -3038,7 +3038,7 @@ Options --annotation strings Key-value pairs of material annotations (key=value) -h, --help help for eval --input stringArray Key-value pairs of policy inputs (key=value) ---kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --material string Path to material or attestation file -p, --policy string Policy reference (./my-policy.yaml, https://my-domain.com/my-policy.yaml, chainloop://my-stored-policy) (default "policy.yaml") --project string Project name to use as engine context for chainloop.* built-ins diff --git a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts index 428b323da..e90eb31de 100644 --- a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts +++ b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts @@ -317,6 +317,11 @@ export enum CraftingSchema_Material_MaterialType { * https://github.com/cobertura/cobertura */ COBERTURA_XML = 41, + /** + * CHECKMARX_JSON - Checkmarx One native JSON report (ScanResultsCollection) + * https://github.com/Checkmarx/ast-cli/blob/main/internal/wrappers/results-json.go + */ + CHECKMARX_JSON = 42, UNRECOGNIZED = -1, } @@ -448,6 +453,9 @@ export function craftingSchema_Material_MaterialTypeFromJSON(object: any): Craft case 41: case "COBERTURA_XML": return CraftingSchema_Material_MaterialType.COBERTURA_XML; + case 42: + case "CHECKMARX_JSON": + return CraftingSchema_Material_MaterialType.CHECKMARX_JSON; case -1: case "UNRECOGNIZED": default: @@ -541,6 +549,8 @@ export function craftingSchema_Material_MaterialTypeToJSON(object: CraftingSchem return "TRUFFLEHOG_JSON"; case CraftingSchema_Material_MaterialType.COBERTURA_XML: return "COBERTURA_XML"; + case CraftingSchema_Material_MaterialType.CHECKMARX_JSON: + return "CHECKMARX_JSON"; case CraftingSchema_Material_MaterialType.UNRECOGNIZED: default: return "UNRECOGNIZED"; diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json index 531471cfc..9d2e5f485 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json @@ -58,7 +58,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" @@ -152,7 +153,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json index bdbcd4c41..19ff7a2ef 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json @@ -58,7 +58,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" @@ -152,7 +153,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json index 49f14380e..ccd767abf 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json @@ -169,7 +169,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json index af52a9ebd..dfcdb0356 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json @@ -169,7 +169,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json index c2dd51e45..4bffd8f75 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json @@ -79,7 +79,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json index 69bf7b496..60086425f 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json @@ -79,7 +79,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json index 059310dca..34c9a1bec 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json @@ -63,7 +63,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json index 8db90b845..3dbeb1aec 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json @@ -63,7 +63,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json index dc6ce0d85..4f685dcae 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json @@ -77,7 +77,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json index fe468a11f..18132852c 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json @@ -77,7 +77,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json index 2da9c7712..6de12fa1b 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json @@ -98,7 +98,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json index 813a5f2f9..f83f3e706 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json @@ -98,7 +98,8 @@ "RADAMSA_REPORT", "RADAMSA_CRASHES", "TRUFFLEHOG_JSON", - "COBERTURA_XML" + "COBERTURA_XML", + "CHECKMARX_JSON" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go index 457219893..9e17497ef 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go @@ -245,6 +245,9 @@ const ( // Cobertura code coverage report in XML format // https://github.com/cobertura/cobertura CraftingSchema_Material_COBERTURA_XML CraftingSchema_Material_MaterialType = 41 + // Checkmarx One native JSON report (ScanResultsCollection) + // https://github.com/Checkmarx/ast-cli/blob/main/internal/wrappers/results-json.go + CraftingSchema_Material_CHECKMARX_JSON CraftingSchema_Material_MaterialType = 42 ) // Enum value maps for CraftingSchema_Material_MaterialType. @@ -292,6 +295,7 @@ var ( 39: "RADAMSA_CRASHES", 40: "TRUFFLEHOG_JSON", 41: "COBERTURA_XML", + 42: "CHECKMARX_JSON", } CraftingSchema_Material_MaterialType_value = map[string]int32{ "MATERIAL_TYPE_UNSPECIFIED": 0, @@ -336,6 +340,7 @@ var ( "RADAMSA_CRASHES": 39, "TRUFFLEHOG_JSON": 40, "COBERTURA_XML": 41, + "CHECKMARX_JSON": 42, } ) @@ -2008,7 +2013,7 @@ var File_workflowcontract_v1_crafting_schema_proto protoreflect.FileDescriptor const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\n" + - ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\xbc\x12\n" + + ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\xd0\x12\n" + "\x0eCraftingSchema\x122\n" + "\x0eschema_version\x18\x01 \x01(\tB\v\xbaH\x06r\x04\n" + "\x02v1\x18\x01R\rschemaVersion\x12N\n" + @@ -2031,7 +2036,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x0fDAGGER_PIPELINE\x10\x06\x12\x15\n" + "\x11TEAMCITY_PIPELINE\x10\a\x12\x13\n" + "\x0fTEKTON_PIPELINE\x10\b\x12\x15\n" + - "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\x87\f\n" + + "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\x9b\f\n" + "\bMaterial\x12[\n" + "\x04type\x18\x01 \x01(\x0e29.workflowcontract.v1.CraftingSchema.Material.MaterialTypeB\f\xbaH\a\x82\x01\x04\x10\x01 \x00\x18\x01R\x04type\x12\x99\x01\n" + "\x04name\x18\x02 \x01(\tB\x84\x01\xbaH\x7f\xba\x01|\n" + @@ -2042,7 +2047,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\vskip_upload\x18\x06 \x01(\bR\n" + "skipUpload\x12\xaa\x01\n" + "\x05group\x18\a \x01(\tB\x93\x01\xbaH\x8f\x01\xba\x01\x8b\x01\n" + - "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\xac\a\n" + + "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\xc0\a\n" + "\fMaterialType\x12\x1d\n" + "\x19MATERIAL_TYPE_UNSPECIFIED\x10\x00\x12\n" + "\n" + @@ -2089,7 +2094,8 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x0eRADAMSA_REPORT\x10&\x12\x13\n" + "\x0fRADAMSA_CRASHES\x10'\x12\x13\n" + "\x0fTRUFFLEHOG_JSON\x10(\x12\x11\n" + - "\rCOBERTURA_XML\x10):\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + + "\rCOBERTURA_XML\x10)\x12\x12\n" + + "\x0eCHECKMARX_JSON\x10*:\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + "\x10CraftingSchemaV2\x128\n" + "\vapi_version\x18\x01 \x01(\tB\x17\xbaH\x14r\x12\n" + "\x10chainloop.dev/v1R\n" + diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto index 086ab4e7b..476132dcd 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto @@ -194,6 +194,9 @@ message CraftingSchema { // Cobertura code coverage report in XML format // https://github.com/cobertura/cobertura COBERTURA_XML = 41; + // Checkmarx One native JSON report (ScanResultsCollection) + // https://github.com/Checkmarx/ast-cli/blob/main/internal/wrappers/results-json.go + CHECKMARX_JSON = 42; } } } diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go index 7faa90aa6..34e51b144 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go @@ -53,6 +53,10 @@ var CraftingMaterialInValidationOrder = []CraftingSchema_Material_MaterialType{ // auto-detection. RADAMSA_CRASHES single-file mode accepts almost any // non-empty file and would eagerly shadow other types; both work fine when // referenced with an explicit kind in a workflow contract. + // NOTE: CHECKMARX_JSON is intentionally omitted from auto-detection. The + // Checkmarx native report is generic JSON that risks shadowing (or being + // shadowed by) other JSON kinds; it must be referenced with an explicit + // --kind CHECKMARX_JSON. Revisit once the fingerprint is proven strong. CraftingSchema_Material_HELM_CHART, CraftingSchema_Material_SARIF, CraftingSchema_Material_BLACKDUCK_SCA_JSON, diff --git a/pkg/attestation/crafter/materials/checkmarx.go b/pkg/attestation/crafter/materials/checkmarx.go new file mode 100644 index 000000000..cb6b1b31d --- /dev/null +++ b/pkg/attestation/crafter/materials/checkmarx.go @@ -0,0 +1,129 @@ +// +// Copyright 2026 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package materials + +import ( + "context" + "encoding/json" + "fmt" + "os" + + schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" + api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1" + "github.com/chainloop-dev/chainloop/pkg/casclient" + "github.com/rs/zerolog" +) + +type CheckmarxCrafter struct { + *crafterCommon + backend *casclient.CASBackend +} + +// checkmarxScanResults is the subset of the Checkmarx One native JSON report +// (the ast-cli ScanResultsCollection produced by `cx ... --report-format json`) +// used to validate its structure. +// https://github.com/Checkmarx/ast-cli/blob/main/internal/wrappers/results-json.go +// +// Results is kept as a json.RawMessage so we can tell an absent "results" key +// (look-alike JSON) apart from a present-but-null or empty one (a valid clean +// scan). encoding/json decodes both an absent key and an explicit null into a +// nil typed slice, so a typed field alone can't distinguish them. The ast-cli +// serializer marshals ScanResultsCollection with no omitempty, and several CLI +// code paths rebuild the slice as nil (marshalling to `"results": null`) when a +// scan or filter yields no findings. +type checkmarxScanResults struct { + ScanID string `json:"scanID"` + TotalCount int `json:"totalCount"` + Results json.RawMessage `json:"results"` +} + +// checkmarxResult is the subset of a single Checkmarx result entry we fingerprint. +// A native report bundles multiple engines (sast, sca, kics, containers, sscs) +// distinguished by Type; every real result carries a Type and a Data payload. +type checkmarxResult struct { + Type string `json:"type"` + SimilarityID string `json:"similarityId"` + Data json.RawMessage `json:"data"` +} + +func NewCheckmarxCrafter(schema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*CheckmarxCrafter, error) { + if schema.Type != schemaapi.CraftingSchema_Material_CHECKMARX_JSON { + return nil, fmt.Errorf("material type is not a Checkmarx native JSON report") + } + craftCommon := &crafterCommon{logger: l, input: schema} + return &CheckmarxCrafter{backend: backend, crafterCommon: craftCommon}, nil +} + +func (i *CheckmarxCrafter) Craft(ctx context.Context, filePath string) (*api.Attestation_Material, error) { + data, err := os.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("can't open the file: %w", err) + } + + var report checkmarxScanResults + if err = json.Unmarshal(data, &report); err != nil { + return nil, fmt.Errorf("invalid Checkmarx native JSON report: %w", ErrInvalidMaterialType) + } + + // Structural fingerprint check. A Checkmarx native report always carries a + // scanID and a results key (an array that may be empty, or null, when nothing + // was found). Auto-detection is intentionally disabled for this kind, but we + // still reject look-alike JSON so an explicit --kind CHECKMARX_JSON fails + // loudly on the wrong file. We anchor on fields that Checkmarx reliably + // populates: the top-level scanID and the presence of the results key. + if report.ScanID == "" || report.Results == nil { + return nil, fmt.Errorf("missing required Checkmarx report fields: %w", ErrInvalidMaterialType) + } + + // Parse the results array. A present-but-null ("null") or empty ("[]") array + // is a valid clean scan (or one whose findings were all filtered out at the + // CLI); both decode to a nil/empty slice without error. + var results []checkmarxResult + if err = json.Unmarshal(report.Results, &results); err != nil { + return nil, fmt.Errorf("invalid Checkmarx results array: %w", ErrInvalidMaterialType) + } + + // Each real result carries a type and a data payload, regardless of engine + // (sast, sca, kics, containers, sscs). similarityId carries omitempty in the + // ast-cli structs and is left as a supporting signal only, to avoid rejecting + // valid reports. + for idx, r := range results { + if r.Type == "" || r.Data == nil { + return nil, fmt.Errorf("checkmarx result %d is missing type or data: %w", idx, ErrInvalidMaterialType) + } + } + + if len(results) == 0 { + i.logger.Debug().Msg("Accepting an empty report.") + } + + // Call uploadAndCraft with the path of the JSON report file + m, err := uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger) + if err != nil { + return nil, err + } + + i.injectAnnotations(m) + + return m, nil +} + +func (i *CheckmarxCrafter) injectAnnotations(m *api.Attestation_Material) { + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + m.Annotations[AnnotationToolNameKey] = "checkmarx" +} diff --git a/pkg/attestation/crafter/materials/checkmarx_test.go b/pkg/attestation/crafter/materials/checkmarx_test.go new file mode 100644 index 000000000..f5ed28a10 --- /dev/null +++ b/pkg/attestation/crafter/materials/checkmarx_test.go @@ -0,0 +1,154 @@ +// +// Copyright 2026 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//nolint:dupl +package materials_test + +import ( + "context" + "testing" + + contractAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials" + "github.com/chainloop-dev/chainloop/pkg/casclient" + mUploader "github.com/chainloop-dev/chainloop/pkg/casclient/mocks" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestNewCheckmarxCrafter(t *testing.T) { + testCases := []struct { + name string + input *contractAPI.CraftingSchema_Material + wantErr bool + }{ + { + name: "happy path", + input: &contractAPI.CraftingSchema_Material{ + Type: contractAPI.CraftingSchema_Material_CHECKMARX_JSON, + }, + }, + { + name: "wrong type", + input: &contractAPI.CraftingSchema_Material{ + Type: contractAPI.CraftingSchema_Material_CONTAINER_IMAGE, + }, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := materials.NewCheckmarxCrafter(tc.input, nil, nil) + if tc.wantErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + }) + } +} + +func TestCheckmarxCrafter_Craft(t *testing.T) { + testCases := []struct { + name string + filePath string + wantErr string + annotations map[string]string + }{ + { + name: "invalid path", + filePath: "./testdata/non-existing.json", + wantErr: "no such file or directory", + }, + { + name: "empty file", + filePath: "./testdata/empty.txt", + wantErr: "invalid Checkmarx native JSON report", + }, + { + name: "wrong content", + filePath: "./testdata/sbom-spdx.json", + wantErr: "missing required Checkmarx report fields", + }, + { + name: "clean scan (empty results array)", + filePath: "./testdata/checkmarx-clean.json", + annotations: map[string]string{ + "chainloop.material.tool.name": "checkmarx", + }, + }, + { + // The ast-cli serializer emits "results": null (not []) when a + // filter/engine path rebuilds an empty (nil) slice. A present-but-null + // results key is still a valid clean report and must be accepted. + name: "clean scan (null results)", + filePath: "./testdata/checkmarx-null-results.json", + annotations: map[string]string{ + "chainloop.material.tool.name": "checkmarx", + }, + }, + { + // Real ast-cli output bundling multiple engines in one file (2 sast, + // 1 sca, 1 kics). SCA results carry a package-based data shape while + // SAST/KICS carry query-based data; the crafter must accept them all. + name: "multi-engine report (sast + sca + kics)", + filePath: "./testdata/checkmarx.json", + annotations: map[string]string{ + "chainloop.material.tool.name": "checkmarx", + }, + }, + } + + schema := &contractAPI.CraftingSchema_Material{ + Name: "test", + Type: contractAPI.CraftingSchema_Material_CHECKMARX_JSON, + } + + l := zerolog.Nop() + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Mock uploader + uploader := mUploader.NewUploader(t) + if tc.wantErr == "" { + uploader.On("Upload", context.TODO(), mock.Anything, mock.Anything, mock.Anything). + Return(&casclient.UpDownStatus{}, nil) + } + + backend := &casclient.CASBackend{Uploader: uploader} + crafter, err := materials.NewCheckmarxCrafter(schema, backend, &l) + require.NoError(t, err) + + got, err := crafter.Craft(context.TODO(), tc.filePath) + if tc.wantErr != "" { + assert.ErrorContains(t, err, tc.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, contractAPI.CraftingSchema_Material_CHECKMARX_JSON.String(), got.MaterialType.String()) + assert.True(t, got.UploadedToCas) + + if tc.annotations != nil { + for k, v := range tc.annotations { + assert.Equal(t, v, got.Annotations[k]) + } + } + }) + } +} diff --git a/pkg/attestation/crafter/materials/materials.go b/pkg/attestation/crafter/materials/materials.go index 48900e5b7..50b925a8d 100644 --- a/pkg/attestation/crafter/materials/materials.go +++ b/pkg/attestation/crafter/materials/materials.go @@ -377,6 +377,8 @@ func Craft(ctx context.Context, materialSchema *schemaapi.CraftingSchema_Materia crafter, err = NewRadamsaCrashesCrafter(materialSchema, casBackend, logger) case schemaapi.CraftingSchema_Material_TRUFFLEHOG_JSON: crafter, err = NewTrufflehogCrafter(materialSchema, casBackend, logger) + case schemaapi.CraftingSchema_Material_CHECKMARX_JSON: + crafter, err = NewCheckmarxCrafter(materialSchema, casBackend, logger) default: return nil, fmt.Errorf("material of type %q not supported yet", materialSchema.Type) } diff --git a/pkg/attestation/crafter/materials/testdata/checkmarx-clean.json b/pkg/attestation/crafter/materials/testdata/checkmarx-clean.json new file mode 100644 index 000000000..98fe69a7c --- /dev/null +++ b/pkg/attestation/crafter/materials/testdata/checkmarx-clean.json @@ -0,0 +1,5 @@ +{ + "results": [], + "totalCount": 0, + "scanID": "dc2b7f5c-625d-4236-891d-90869454aaf7" +} diff --git a/pkg/attestation/crafter/materials/testdata/checkmarx-null-results.json b/pkg/attestation/crafter/materials/testdata/checkmarx-null-results.json new file mode 100644 index 000000000..8c5bf2422 --- /dev/null +++ b/pkg/attestation/crafter/materials/testdata/checkmarx-null-results.json @@ -0,0 +1,5 @@ +{ + "results": null, + "totalCount": 0, + "scanID": "dc2b7f5c-625d-4236-891d-90869454aaf7" +} diff --git a/pkg/attestation/crafter/materials/testdata/checkmarx.json b/pkg/attestation/crafter/materials/testdata/checkmarx.json new file mode 100644 index 000000000..71fa66648 --- /dev/null +++ b/pkg/attestation/crafter/materials/testdata/checkmarx.json @@ -0,0 +1,299 @@ +{ + "results": [ + { + "type": "sast", + "label": "sast", + "id": "13588362", + "similarityId": "1959005240", + "status": "NEW", + "state": "TO_VERIFY", + "severity": "HIGH", + "created": "2023-11-29T16:00:29Z", + "firstFoundAt": "2023-11-24T10:27:23Z", + "foundAt": "2023-11-29T16:00:29Z", + "firstScanId": "12c2e25b-d25a-4321-9ce2-79a4df314d38", + "description": "The method Lambda embeds untrusted data in generated output with html, at line 18 of /src/main/resources/lessons/challenges/js/challenge8.js. This untrusted data is embedded into the output without proper sanitization or encoding, enabling an attacker to inject malicious code into the generated web-page.\n\n", + "descriptionHTML": "

The method Lambda embeds untrusted data in generated output with html, at line 18 of /src/main/resources/lessons/challenges/js/challenge8.js. This untrusted data is embedded into the output without proper sanitization or encoding, enabling an attacker to inject malicious code into the generated web-page.

\n", + "data": { + "queryId": 1779421333800271656, + "queryName": "Client_DOM_Stored_XSS", + "group": "JavaScript_High_Risk", + "resultHash": "vu/B9/LA3CAJTW+YXs3WsnzYc14=", + "languageName": "JavaScript", + "nodes": [ + { + "id": "I09eTl48jtJ+UqDgeyBVAQ/oiYI=", + "line": 7, + "name": "votes", + "column": 43, + "length": 5, + "method": "Lambda", + "nodeID": 167070, + "domType": "ParamDecl", + "fileName": "/challenge8-js.testdata", + "fullName": "CxJSNS_953c03de.loadVotes.Lambda.votes", + "typeName": "object", + "methodLine": 7, + "definitions": "1" + }, + { + "id": "RJLk6yhAIFf46AYFkZGpG5BITrE=", + "line": 18, + "name": "votes", + "column": 42, + "length": 5, + "method": "Lambda", + "nodeID": 167232, + "domType": "UnknownReference", + "fileName": "/challenge8-js.testdata", + "fullName": "CxJSNS_953c03de.loadVotes.Lambda.votes", + "typeName": "object", + "methodLine": 7, + "definitions": "1" + }, + { + "id": "wu4zwsJQtEpEwABrFtX8o1/8u8E=", + "line": 18, + "name": "html", + "column": 37, + "length": 4, + "method": "Lambda", + "nodeID": 167226, + "domType": "MethodInvokeExpr", + "fileName": "/challenge8-js.testdata", + "fullName": "html", + "typeName": "html", + "methodLine": 7, + "definitions": "0" + } + ] + }, + "comments": {}, + "vulnerabilityDetails": { + "cweId": 79, + "cvss": {}, + "compliances": [ + "FISMA 2014", + "MOIS(KISA) Secure Coding 2021", + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "NIST SP 800-53", + "OWASP ASVS", + "CWE top 25", + "ASD STIG 4.10", + "SANS top 25", + "OWASP Top 10 2017" + ] + } + }, + { + "type": "sast", + "label": "sast", + "id": "13588363", + "similarityId": "15011039", + "status": "NEW", + "state": "TO_VERIFY", + "severity": "HIGH", + "created": "2023-11-29T16:00:29Z", + "firstFoundAt": "2023-11-24T10:27:23Z", + "foundAt": "2023-11-29T16:00:29Z", + "firstScanId": "12c2e25b-d25a-4321-9ce2-79a4df314d38", + "description": "The method Lambda embeds untrusted data in generated output with html, at line 57 of /src/main/resources/webgoat/static/js/goatApp/support/GoatUtils.js. This untrusted data is embedded into the output without proper sanitization or encoding, enabling an attacker to inject malicious code into the generated web-page.\n\n", + "descriptionHTML": "

The method Lambda embeds untrusted data in generated output with html, at line 57 of /src/main/resources/webgoat/static/js/goatApp/support/GoatUtils.js. This untrusted data is embedded into the output without proper sanitization or encoding, enabling an attacker to inject malicious code into the generated web-page.

\n", + "data": { + "queryId": 1779421333800271656, + "queryName": "Client_DOM_Stored_XSS", + "group": "JavaScript_High_Risk", + "resultHash": "jFgXDclJK/RDlpwY3fbow/7lVv8=", + "languageName": "JavaScript", + "nodes": [ + { + "id": "voHHLkc4qjb3UpedvmZQayOk03I=", + "line": 56, + "name": "reply", + "column": 69, + "length": 5, + "method": "Lambda", + "nodeID": 183497, + "domType": "ParamDecl", + "fileName": "/GoatUtils-js.testdata", + "fullName": "CxJSNS_b114a3b8.CxAssociativeArray_75dc0652.Cxc09a0906.Lambda.reply", + "typeName": "object", + "methodLine": 56, + "definitions": "1" + }, + { + "id": "IbtfjyTk3jy5XU57DiAGTLLtJKY=", + "line": 57, + "name": "reply", + "column": 51, + "length": 5, + "method": "Lambda", + "nodeID": 183516, + "domType": "UnknownReference", + "fileName": "/GoatUtils-js.testdata", + "fullName": "CxJSNS_b114a3b8.CxAssociativeArray_75dc0652.Cxc09a0906.Lambda.reply", + "typeName": "object", + "methodLine": 56, + "definitions": "1" + }, + { + "id": "ZMHi6gwUdqfEtqo08wbDlnaGuKA=", + "line": 57, + "name": "html", + "column": 46, + "length": 4, + "method": "Lambda", + "nodeID": 183512, + "domType": "MethodInvokeExpr", + "fileName": "/GoatUtils-js.testdata", + "fullName": "CxJSNS_b114a3b8.CxAssociativeArray_75dc0652.Cxc09a0906.Lambda.html", + "typeName": "html", + "methodLine": 56, + "definitions": "0" + } + ] + }, + "comments": {}, + "vulnerabilityDetails": { + "cweId": 79, + "cvss": {}, + "compliances": [ + "FISMA 2014", + "MOIS(KISA) Secure Coding 2021", + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "NIST SP 800-53", + "OWASP ASVS", + "CWE top 25", + "ASD STIG 4.10", + "SANS top 25", + "OWASP Top 10 2017" + ] + } + }, + { + "type": "kics", + "label": "IaC Security", + "id": "16350365", + "similarityId": "531bf8e9771fc9a38b866afcdc86e10dd80487262d98baf44f82522516f4db9e", + "status": "NEW", + "state": "TO_VERIFY", + "severity": "HIGH", + "created": "2023-11-29T15:54:59Z", + "firstFoundAt": "2023-11-29T15:54:59Z", + "foundAt": "2023-11-29T15:54:59Z", + "firstScanId": "dc2b7f5c-625d-4236-891d-90869454aaf7", + "description": "A user should be specified in the dockerfile, otherwise the image will run as root", + "descriptionHTML": "

A user should be specified in the dockerfile, otherwise the image will run as root

\n", + "data": { + "queryId": "fd54f200-402c-4333-a5a4-36ef6709af2f [Taken from query_id]", + "queryName": "Missing User Instruction", + "group": "Build Process [Taken from category]", + "line": 1, + "platform": "Dockerfile", + "issueType": "MissingAttribute", + "expectedValue": "The 'Dockerfile' should contain the 'USER' instruction", + "value": "The 'Dockerfile' does not contain any 'USER' instruction", + "filename": "/Dockerfile_desktop" + }, + "comments": {}, + "vulnerabilityDetails": { + "cvss": {} + } + }, + { + "type": "sca", + "scaType": "Vulnerability", + "label": "sca", + "id": "CVE-2013-7285", + "similarityId": "CVE-2013-7285", + "status": "NEW", + "state": "TO_VERIFY", + "severity": "HIGH", + "created": "2023-11-29T15:58:19Z", + "firstFoundAt": "2023-11-24T10:25:10Z", + "foundAt": "2023-11-29T15:58:19Z", + "firstScanId": "12c2e25b-d25a-4321-9ce2-79a4df314d38", + "description": "Xstream API versions up to 1.4.6, if the security framework has not been initialized, may allow a remote attacker to run arbitrary shell commands by manipulating the processed input stream when unmarshaling XML or any supported format. e.g. JSON.", + "descriptionHTML": "

Xstream API versions up to 1.4.6, if the security framework has not been initialized, may allow a remote attacker to run arbitrary shell commands by manipulating the processed input stream when unmarshaling XML or any supported format. e.g. JSON.

\n", + "data": { + "packageData": [ + { + "comment": "https://github.com/advisories/GHSA-f554-x222-wgf7", + "type": "Advisory", + "url": "https://github.com/advisories/GHSA-f554-x222-wgf7" + }, + { + "comment": "https://x-stream.github.io/CVE-2013-7285.html", + "type": "Advisory", + "url": "https://x-stream.github.io/CVE-2013-7285.html" + }, + { + "comment": "http://blog.diniscruz.com/2013/12/xstream-remote-code-execution-exploit.html", + "type": "Disclosure", + "url": "http://blog.diniscruz.com/2013/12/xstream-remote-code-execution-exploit.html" + }, + { + "comment": "https://www.mail-archive.com/user@xstream.codehaus.org/msg00604.html", + "type": "Mail Thread", + "url": "https://www.mail-archive.com/user@xstream.codehaus.org/msg00604.html" + }, + { + "comment": "https://www.mail-archive.com/user@xstream.codehaus.org/msg00607.html", + "type": "Mail Thread", + "url": "https://www.mail-archive.com/user@xstream.codehaus.org/msg00607.html" + }, + { + "comment": "https://github.com/x-stream/xstream/commit/94666ae6dfe839410c73bdfeeb211374f04a2059", + "type": "Commit", + "url": "https://github.com/x-stream/xstream/commit/94666ae6dfe839410c73bdfeeb211374f04a2059" + } + ], + "packageIdentifier": "Maven-com.thoughtworks.xstream:xstream-1.4.5", + "scaPackageData": { + "id": "Maven-com.thoughtworks.xstream:xstream-1.4.5", + "fixLink": "https://devhub.checkmarx.com/cve-details/CWE-78", + "locations": [ + "pom.xml" + ], + "dependencyPaths": [ + [ + { + "id": "Maven-com.thoughtworks.xstream:xstream-1.4.5", + "name": "com.thoughtworks.xstream:xstream", + "version": "1.4.5", + "isResolved": true, + "locations": [ + "pom.xml" + ] + } + ] + ], + "outdated": true, + "supportsQuickFix": false, + "isDirectDependency": true, + "typeOfDependency": "Direct Dependency" + }, + "recommendedVersion": "1.4.20" + }, + "comments": {}, + "vulnerabilityDetails": { + "cweId": "CWE-78", + "cvssScore": 9.8, + "cveName": "CWE-78", + "cvss": { + "version": 3, + "attackVector": "NETWORK", + "availability": "HIGH", + "confidentiality": "HIGH", + "attackComplexity": "LOW" + } + } + } + ], + "totalCount": 4, + "scanID": "dc2b7f5c-625d-4236-891d-90869454aaf7" +}