From dd888597c903c99c003d37a540020879bfd043db Mon Sep 17 00:00:00 2001 From: Virgile Prevosto <virgile.prevosto@m4x.org> Date: Wed, 22 Jul 2020 19:14:13 +0200 Subject: [PATCH] [sarif] on the road to sarif 2.1.0 migration --- src/plugins/markdown-report/sarif.ml | 435 +++++++++++++++-------- src/plugins/markdown-report/sarif_gen.ml | 28 +- 2 files changed, 297 insertions(+), 166 deletions(-) diff --git a/src/plugins/markdown-report/sarif.ml b/src/plugins/markdown-report/sarif.ml index b41a31c18ac..0a778905e0f 100644 --- a/src/plugins/markdown-report/sarif.ml +++ b/src/plugins/markdown-report/sarif.ml @@ -32,6 +32,15 @@ module type Json_type = sig val to_yojson: t -> Yojson.Safe.t end +module Json_string: Json_type with type t = string = +struct + type t = string + let of_yojson = function + | `String s -> Ok s + | _ -> Error "string" + let to_yojson s = `String s +end + module Json_dictionary(J: Json_type): Json_type with type t = (string * J.t) list = struct @@ -55,6 +64,8 @@ struct `Assoc json_l end +module JsonStringDictionary = Json_dictionary(Json_string) + module Uri: sig include Json_type with type t = private string val sarif_github:t @@ -124,13 +135,80 @@ module ArtifactLocation = struct create ~uri () end -module FileContent = struct +module ArtifactLocationDictionary = Json_dictionary(ArtifactLocation) + +module Custom_properties = + Json_dictionary(struct + type t = Yojson.Safe.t + let of_yojson x = Ok x + let to_yojson x = x + end) + +module Properties = struct + type tags = string list [@@deriving yojson] + + type t = { + tags: tags; + additional_properties: Custom_properties.t + } + + let default = { tags = []; additional_properties = [] } + + let create additional_properties = + let tags = List.map fst additional_properties in + { tags; additional_properties } + + let of_yojson = function + | `Null -> Ok default + | `Assoc l -> + (match List.assoc_opt "tags" l with + | None -> Error "properties" + | Some json -> + (match tags_of_yojson json with + | Ok tags -> + let additional_properties = List.remove_assoc "tags" l in + Ok { tags; additional_properties } + | Error loc -> Error ("properties." ^ loc))) + | _ -> Error "properties" + + let to_yojson { tags; additional_properties } = + match tags with + | [] -> `Null + | _ -> `Assoc (("tags", tags_to_yojson tags)::additional_properties) +end + +module MultiformatMessageString = struct + type t = { + text: string; + markdown: (string [@default ""]); + properties: (Properties.t [@default Properties.default]) + }[@@deriving yojson] + + let create ~text ?(markdown="") ?(properties=Properties.default) () = + { text; markdown; properties } + + let default = create ~text:"default" () +end + +module MultiformatMessageStringDictionary = + Json_dictionary(MultiformatMessageString) + +module ArtifactContent = struct type t = - | Text of string [@name "text"] - | Binary of string [@name "binary"] + { text: (string [@default ""]); + binary: (string [@default ""]); + rendered: + (MultiformatMessageString.t [@default MultiformatMessageString.default]); + properties: (Properties.t [@default Properties.default]) + } [@@deriving yojson] - let default = Text "" + let create ?(text="") ?(binary="") + ?(rendered=MultiformatMessageString.default) + ?(properties=Properties.default) () = + { text; binary; rendered; properties } + + let default = create () end module Region = struct @@ -143,7 +221,7 @@ module Region = struct charLength: (int [@default 0]); byteOffset: (int [@default 0]); byteLength: (int [@default 0]); - snippet: (FileContent.t [@default FileContent.default]); + snippet: (ArtifactContent.t [@default ArtifactContent.default]); message: (Message.t [@default Message.default]) }[@@deriving yojson] @@ -156,7 +234,7 @@ module Region = struct ?(charLength = 0) ?(byteOffset = 0) ?(byteLength = 0) - ?(snippet = FileContent.default) + ?(snippet = ArtifactContent.default) ?(message = Message.default) () = @@ -187,46 +265,6 @@ module Rectangle = struct [@@deriving yojson] end -module Custom_properties = - Json_dictionary(struct - type t = Yojson.Safe.t - let of_yojson x = Ok x - let to_yojson x = x - end) - -module Properties = struct - type tags = string list [@@deriving yojson] - - type t = { - tags: tags; - additional_properties: Custom_properties.t - } - - let default = { tags = []; additional_properties = [] } - - let create additional_properties = - let tags = List.map fst additional_properties in - { tags; additional_properties } - - let of_yojson = function - | `Null -> Ok default - | `Assoc l -> - (match List.assoc_opt "tags" l with - | None -> Error "properties" - | Some json -> - (match tags_of_yojson json with - | Ok tags -> - let additional_properties = List.remove_assoc "tags" l in - Ok { tags; additional_properties } - | Error loc -> Error ("properties." ^ loc))) - | _ -> Error "properties" - - let to_yojson { tags; additional_properties } = - match tags with - | [] -> `Null - | _ -> `Assoc (("tags", tags_to_yojson tags)::additional_properties) -end - module PhysicalLocation = struct type t = { id: (string [@default ""]); @@ -659,42 +697,47 @@ end module Replacement = struct type t = { deletedRegion: Region.t; - insertedContent: (FileContent.t [@default FileContent.default]) + insertedContent: (ArtifactContent.t [@default ArtifactContent.default]) }[@@deriving yojson] end -module File = struct +module Artifact = struct type t = { - artifactLocation: (ArtifactLocation.t [@default ArtifactLocation.default]); - parentKey: (string [@default ""]); + description: (Message.t [@default Message.default]); + location: (ArtifactLocation.t [@default ArtifactLocation.default]); + parentIndex: (int [@default -1]); offset: (int [@default 0]); - length: (int [@default 0]); + length: (int [@default -1]); roles: (Role.t list [@default []]); mimeType: (string [@default ""]); - contents: (FileContent.t [@default FileContent.default]); + contents: (ArtifactContent.t [@default ArtifactContent.default]); encoding: (string [@default ""]); - hashes: (Hash.t list [@default []]); - lastModifiedTime: (string [@default ""]); + sourceLanguage: (string [@default ""]); + hashes: (JsonStringDictionary.t [@default []]); + lastModifiedTimeUtc: (string [@default ""]); properties: (Properties.t [@default Properties.default]); }[@@deriving yojson] let create - ?(artifactLocation = ArtifactLocation.default) - ?(parentKey = "") + ?(description = Message.default) + ?(location = ArtifactLocation.default) + ?(parentIndex = -1) ?(offset = 0) - ?(length = 0) + ?(length = -1) ?(roles = []) ?(mimeType = "") - ?(contents = FileContent.default) + ?(contents = ArtifactContent.default) ?(encoding = "") + ?(sourceLanguage = "") ?(hashes = []) - ?(lastModifiedTime = "") + ?(lastModifiedTimeUtc = "") ?(properties = Properties.default) () = { - artifactLocation; parentKey; offset; length; roles; mimeType; contents; - encoding; hashes; lastModifiedTime; properties + description; location; parentIndex; offset; length; roles; mimeType; + contents; encoding; sourceLanguage; hashes; lastModifiedTimeUtc; + properties } end @@ -737,97 +780,139 @@ end module RuleConfigLevel: sig include Json_type with type t = private string + val cl_none: t val cl_note: t val cl_warning: t val cl_error: t - val cl_open: t end = struct type t = string [@@deriving yojson] + let cl_none = "none" let cl_note = "note" let cl_warning = "warning" let cl_error = "error" - let cl_open = "open" end -module RuleConfiguration = struct +module ReportingConfiguration = struct type t = { enabled: (bool [@default false]); - defaultLevel: (RuleConfigLevel.t [@default RuleConfigLevel.cl_open]); - parameters: (Properties.t [@default Properties.default]) + defaultLevel: (RuleConfigLevel.t [@default RuleConfigLevel.cl_none]); + rank: (int [@default -1]); + parameters: (Properties.t [@default Properties.default]); + properties: (Properties.t [@default Properties.default]); }[@@deriving yojson] let default = { enabled = false; - defaultLevel = RuleConfigLevel.cl_open; + defaultLevel = RuleConfigLevel.cl_none; + rank = -1; parameters = Properties.default; + properties = Properties.default; } end -module Rule = struct +module ToolComponentReference =struct +type t = { + name: (string [@default ""]); + index: (int [@default -1]); + guid: (string [@default ""]); + properties: (Properties.t [@default Properties.default]); +}[@@deriving yojson] + +let create + ?(name="") ?(index = -1) ?(guid = "") ?(properties=Properties.default) () = + { name; index; guid; properties } + +let default = create () + +end + +module ReportingDescriptorReference = +struct type t = { id: (string [@default ""]); - name: (string [@default ""]); - shortDescription: (Message.t [@default Message.default]); - fullDescription: (Message.t [@default Message.default]); - messageStrings: - (Additional_properties.t [@default Additional_properties.default]); - richMessageStrings: - (Additional_properties.t [@default Additional_properties.default]); - configuration: (RuleConfiguration.t [@default RuleConfiguration.default]); - helpUri: (string [@default ""]); + index: (int [@default -1]); + guid: (string [@default ""]); + toolComponent: + (ToolComponentReference.t [@default ToolComponentReference.default]); properties: (Properties.t [@default Properties.default]); }[@@deriving yojson] - let default = { - id = ""; - name = ""; - shortDescription = Message.default; - fullDescription = Message.default; - messageStrings = Additional_properties.default; - richMessageStrings = Additional_properties.default; - configuration = RuleConfiguration.default; - helpUri = ""; - properties = Properties.default; - } - let create - ~id - ?(name="") - ?(shortDescription=Message.default) - ?(fullDescription=Message.default) - ?(messageStrings=Additional_properties.default) - ?(richMessageStrings=Additional_properties.default) - ?(configuration=RuleConfiguration.default) - ?(helpUri="") - ?(properties=Properties.default) - () - = - { id; name; shortDescription; fullDescription; messageStrings; - richMessageStrings; configuration; helpUri; properties } + ?(id="") ?(index = -1) ?(guid="") + ?(toolComponent=ToolComponentReference.default) + ?(properties=Properties.default) () = + { id; index; guid; toolComponent; properties } + let default = create () end -module Rule_dictionary = Json_dictionary(Rule) +module ReportingDescriptorRelationship = struct + type t = { + target: ReportingDescriptorReference.t; + kinds: (string list [@default ["relevant"]]); + description: (Message.t [@default Message.default]); + properties: (Properties.t [@default Properties.default]); + }[@@deriving yojson] + + let create + ~target + ?(kinds=["relevant"]) + ?(description=Message.default) + ?(properties=Properties.default) () = + { target; kinds; description; properties } -module Resources = struct + let default = create ~target:ReportingDescriptorReference.default () +end + +module ReportingDescriptor = struct type t = { + id: string; + deprecatedIds: (string list [@default []]); + guid: (string [@default ""]); + deprecatedGuids: (string list [@default []]); + name: (string [@default ""]); + deprecatedNames: (string list [@default []]); + shortDescription: + (MultiformatMessageString.t [@default MultiformatMessageString.default]); + fullDescription: + (MultiformatMessageString.t [@default MultiformatMessageString.default]); messageStrings: - (Additional_properties.t [@default Additional_properties.default]); - rules: (Rule_dictionary.t [@default []]); + (MultiformatMessageStringDictionary.t [@default []]); + defaultConfiguration: + (ReportingConfiguration.t [@default ReportingConfiguration.default]); + helpUri: (string [@default ""]); + help: + (MultiformatMessageString.t [@default MultiformatMessageString.default]); + relationships: + (ReportingDescriptorRelationship.t list [@default []]); + properties: (Properties.t [@default Properties.default]); }[@@deriving yojson] - let default = { - messageStrings = Additional_properties.default; - rules = [] } - let create - ?(messageStrings=Additional_properties.default) - ?(rules=[]) + ~id + ?(deprecatedIds=[]) + ?(guid="") + ?(deprecatedGuids=[]) + ?(name="") + ?(deprecatedNames=[]) + ?(shortDescription=MultiformatMessageString.default) + ?(fullDescription=MultiformatMessageString.default) + ?(messageStrings=[]) + ?(defaultConfiguration=ReportingConfiguration.default) + ?(helpUri="") + ?(help=MultiformatMessageString.default) + ?(relationships=[]) + ?(properties=Properties.default) () = - { messageStrings; rules } + { id; deprecatedIds; guid; deprecatedGuids; name; deprecatedNames; + shortDescription; fullDescription; messageStrings; + defaultConfiguration; helpUri; help; relationships; properties } + + let default = create ~id:"id" () + end module Result_kind: @@ -979,10 +1064,6 @@ module VersionControlDetails = struct }[@@deriving yojson] end -module File_dictionary = Json_dictionary(File) - -module LogicalLocation_dictionary = Json_dictionary(LogicalLocation) - module ColumnKind: sig include Json_type with type t = private string val utf16CodeUnits: t @@ -995,65 +1076,117 @@ struct let unicodeCodePoints = "unicodeCodePoints" end +module RunAutomationDetails = struct + type t = unit [@@deriving yojson] + let default = () +end + +module ExternalPropertyFileReferences = struct + type t = unit [@@deriving yojson] + let default = () +end + +module ToolComponent = struct + type t = unit [@@deriving yojson] + let create () = () + let default = () +end + +module Address = struct + type t = unit [@@deriving yojson] + let default = [] +end + +module WebRequest = struct + type t = unit [@@deriving yojson] +end + +module WebResponse = struct + type t = unit [@@deriving yojson] +end + +module SpecialLocations = struct + type t = unit [@@deriving yojson] + let default = () +end + module Run = struct type t = { tool: Tool.t; invocations: (Invocation.t list [@default []]); conversion: (Conversion.t [@default Conversion.default]); + language: (string [@default "en-US"]); versionControlProvenance: (VersionControlDetails.t list [@default []]); originalUriBaseIds: - (Additional_properties.t [@default Additional_properties.default]); - files: (File_dictionary.t [@default []]); - logicalLocations: (LogicalLocation_dictionary.t [@default []]); - graphs: (Graph_dictionary.t [@default []]); + (ArtifactLocationDictionary.t [@default []]); + artifacts: (Artifact.t list [@default []]); + logicalLocations: (LogicalLocation.t list [@default []]); + graphs: (Graph.t list [@default []]); results: (Sarif_result.t list [@default []]); - resources: (Resources.t [@default Resources.default]); - instanceGuid: (string [@default ""]); - correlationGuid: (string [@default ""]); - logicalId: (string [@default ""]); - description: (Message.t [@default Message.default]); - automationLogicalId: (string [@default ""]); - baselineInstanceGuid: (string [@default ""]); - architecture: (string [@default ""]); - richMessageMimeType: (string [@default "text/markdown;variant=GFM" ]); - redactionToken: (string [@default ""]); - defaultFileEncoding: (string [@default "utf-8"]); + automationDetails: + (RunAutomationDetails.t [@default RunAutomationDetails.default]); + runAggregates: (RunAutomationDetails.t list [@default []]); + baselineGuid: (string [@default ""]); + redactionToken: (string list [@default []]); + defaultEncoding: (string [@default "utf-8"]); + defaultSourceLanguage: (string [@default ""]); + newlineSequences: (string list [@default ["\r\n"; "\n"]]); columnKind: (ColumnKind.t [@default ColumnKind.unicodeCodePoints]); + externalPropertyFileReferences: + (ExternalPropertyFileReferences.t + [@default ExternalPropertyFileReferences.default]); + threadFlowLocations: (ThreadFlowLocation.t list [@default []]); + taxonomies: (ToolComponent.t list [@default []]); + addresses: (Address.t list [@default []]); + translations: (ToolComponent.t list [@default []]); + policies: (ToolComponent.t list [@default[]]); + webRequests: (WebRequest.t list [@default[]]); + webResponses: (WebResponse.t list [@default[]]); + specialLocations: (SpecialLocations.t [@default SpecialLocations.default]); properties: (Properties.t [@default Properties.default]); } [@@deriving yojson] let create ~tool - ~invocations + ?(invocations=[]) ?(conversion=Conversion.default) + ?(language="en-US") ?(versionControlProvenance=[]) - ?(originalUriBaseIds=Additional_properties.default) - ?(files=[]) + ?(originalUriBaseIds=[]) + ?(artifacts=[]) ?(logicalLocations=[]) ?(graphs=[]) ?(results=[]) - ?(resources=Resources.default) - ?(instanceGuid="") - ?(correlationGuid="") - ?(logicalId="") - ?(description=Message.default) - ?(automationLogicalId="") - ?(baselineInstanceGuid="") - ?(architecture="") - ?(richMessageMimeType="text/markdown;variant=GFM") - ?(redactionToken="") - ?(defaultFileEncoding="utf-8") + ?(automationDetails=RunAutomationDetails.default) + ?(runAggregates=[]) + ?(baselineGuid="") + ?(redactionToken=[]) + ?(defaultEncoding="utf-8") + ?(defaultSourceLanguage="C") + ?(newlineSequences=["\r\n"; "\n"]) ?(columnKind=ColumnKind.unicodeCodePoints) + ?(externalPropertyFileReferences=ExternalPropertyFileReferences.default) + ?(threadFlowLocations=[]) + ?(taxonomies=[]) + ?(addresses=[]) + ?(translations=[]) + ?(policies=[]) + ?(webRequests=[]) + ?(webResponses=[]) + ?(specialLocations=SpecialLocations.default) ?(properties=Properties.default) () = { - tool; invocations; conversion; versionControlProvenance; originalUriBaseIds; - files; logicalLocations; graphs; results; resources; instanceGuid; - correlationGuid; logicalId; description; automationLogicalId; - baselineInstanceGuid; architecture; richMessageMimeType; - redactionToken; defaultFileEncoding; columnKind; properties + tool; invocations; conversion; versionControlProvenance; + language; originalUriBaseIds; + artifacts; logicalLocations; graphs; results; + automationDetails; runAggregates; baselineGuid; redactionToken; + defaultEncoding; defaultSourceLanguage; newlineSequences; columnKind; + externalPropertyFileReferences; threadFlowLocations; taxonomies; + addresses; translations; policies; webRequests; webResponses; + specialLocations; properties; } end diff --git a/src/plugins/markdown-report/sarif_gen.ml b/src/plugins/markdown-report/sarif_gen.ml index 7204ae3f339..7c47c507a1a 100644 --- a/src/plugins/markdown-report/sarif_gen.ml +++ b/src/plugins/markdown-report/sarif_gen.ml @@ -156,26 +156,23 @@ let gen_statuses () = in List.rev (Property_status.fold f []) -let gen_files () = +let gen_artifacts () = let add_src_file f = - let key = - let fname = Filepath.Normalized.to_pretty_string f in - Filename.chop_extension (Filename.basename fname) - in - let artifactLocation = ArtifactLocation.create ~uri:(f :> string) () in + let uri = (f:Filepath.Normalized.t :> string) in + let location = ArtifactLocation.create ~uri () in let roles = [ Role.analysisTarget ] in let mimeType = "text/x-csrc" in - key, File.create ~artifactLocation ~roles ~mimeType () + Artifact.create ~location ~roles ~mimeType () in List.map add_src_file (Kernel.Files.get ()) let add_rule id desc l = let text = desc ^ "." in - let shortDescription = Message.plain_text ~text () in - let rule = Rule.create ~id ~shortDescription () in - (id, rule) :: l + let shortDescription = MultiformatMessageString.create ~text () in + let rule = ReportingDescriptor.create ~id ~shortDescription () in + rule :: l -let make_rule_dictionary rules = Datatype.String.Map.fold add_rule rules [] +let make_taxonomies rules = Datatype.String.Map.fold add_rule rules [] let gen_run remarks = let tool = frama_c_sarif in @@ -189,11 +186,12 @@ let gen_run remarks = Datatype.String.Map.add "user-spec" "User-written ACSL specification" rules in - let rules = make_rule_dictionary rules in - let resources = Resources.create ~rules () in + let rules = make_taxonomies rules in + ignore(rules); + let taxonomies = [ToolComponent.create (* ~rules*) ()] in let results = results @ user_annot_results in - let files = gen_files () in - Run.create ~tool ~invocations ~results ~resources ~files () + let artifacts = gen_artifacts () in + Run.create ~tool ~invocations ~results ~taxonomies ~artifacts () let generate () = let remarks = get_remarks () in -- GitLab