From ccea170c14ec44a5da57b2eaeb20cad98823e418 Mon Sep 17 00:00:00 2001 From: Virgile Prevosto <virgile.prevosto@m4x.org> Date: Fri, 7 Dec 2018 16:16:46 +0100 Subject: [PATCH] [sarif] initial version of the sarif schema implementation --- src/plugins/markdown-report/sarif.ml | 509 +++++++++++++++++++++++++-- 1 file changed, 481 insertions(+), 28 deletions(-) diff --git a/src/plugins/markdown-report/sarif.ml b/src/plugins/markdown-report/sarif.ml index f8c4c2b3613..5d47b2dd8ff 100644 --- a/src/plugins/markdown-report/sarif.ml +++ b/src/plugins/markdown-report/sarif.ml @@ -13,25 +13,69 @@ type version = [@@deriving yojson] (* not defined yet *) -type message = { __body: string }[@@deriving yojson] -let no_msg = { __body = "" } +type message = { + text: (string [@default ""]); + messageId: (string [@default ""]); + richText: (string [@default ""]); + richMessageId: (string [@default ""]); + arguments: (string list [@default []]); +}[@@deriving yojson] -type fileLocation = { __filename: string }[@@deriving yojson] -let unknown_file = { __filename = "" } +let no_msg = + { text = ""; + messageId = ""; + richText = ""; + richMessageId = ""; + arguments = []; + } -type region = { __line: int }[@@deriving yojson] +type fileLocation = { + uri: string; + uriBaseId: (string [@default ""]) + }[@@deriving yojson] +let unknown_file = { uri = ""; uriBaseId = "" } -type rectangle = { __minx: int; __maxx: int; __miny: int; __maxy: int } +type fileContent = + | Text of string [@name "text"] + | Binary of string [@name "binary"] [@@deriving yojson] -type threadFlow = { __id: int }[@@deriving yojson] +let no_file_content = Text "" -type attachment = { - description: (message [@default no_msg ]); - fileLocation: fileLocation; - regions: (region list [@default []]); - rectangles: (rectangle list [@default []]) -} [@@deriving yojson] +type region = { + startLine: (int [@default 0]); + startColumn: (int [@default 0]); + endLine: (int [@default 0]); + endColumn: (int [@default 0]); + charOffset: (int [@default 0]); + charLength: (int [@default 0]); + byteOffset: (int [@default 0]); + byteLength: (int [@default 0]); + snippet: (fileContent [@default no_file_content]); + message: (message [@default no_msg]) +}[@@deriving yojson] + +let no_region = { + startLine = 0; + startColumn = 0; + endLine = 0; + endColumn = 0; + charOffset = 0; + charLength = 0; + byteOffset = 0; + byteLength = 0; + snippet = no_file_content; + message = no_msg; +} + +type rectangle = { + top: (float [@default 0.]); + left: (float [@default 0.]); + bottom: (float [@default 0.]); + right: (float [@default 0.]); + message: (message [@default no_msg]); +} +[@@deriving yojson] type custom_properties = [ `Null | `Assoc of (string * Yojson.Safe.json) list ] [@@deriving yojson] @@ -44,16 +88,207 @@ type properties = { let no_prop = { tags = []; additional_properties = `Null } +type physicalLocation = { + id: (string [@default ""]); + fileLocation: fileLocation; + region: (region [@default no_region]); + contextRegion: (region [@default no_region]); +}[@@deriving yojson] +let unknown_physicalLocation = { + id = ""; + fileLocation = unknown_file; + region = no_region; + contextRegion = no_region; +} + +type location = { + physicalLocation: physicalLocation; + fullyQualifiedLogicalName: (string [@default ""]); + message: (message [@default no_msg]); + annotations: (region list [@default []]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] +let no_loc = { + physicalLocation = unknown_physicalLocation; + fullyQualifiedLogicalName = ""; + message = no_msg; + annotations = []; + properties = no_prop +} + +type stackFrame = { + location: (location [@default no_loc]); + stack_module: (string [@default ""])[@key "module"]; + threadId: (int [@default 0]); + address: (int [@default 0]); + offset: (int [@default 0]); + parameters: (string list [@default []]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type stack = { + message: (message [@default no_msg]); + frames: stackFrame list; + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +let no_stack = { + message = no_msg; + frames = []; + properties = no_prop +} + +(* TODO: this type definition is unclear in the schema. *) +type additional_properties = + { additionalProperties: string }[@@deriving yojson] + +let no_additional_prop = { additionalProperties = "" } + +type stl_importance = + | Important [@name "important"] + | Essential [@name "essential"] + | Unimportant [@name "unimportant"] +[@@deriving yojson] + +type threadFlowLocation = { + step: int; + location: (location [@default no_loc]); + stack: (stack [@default no_stack]); + kind: (string [@default ""]); + tfl_module: (string [@default ""])[@key "module"]; + state: (additional_properties [@default no_additional_prop]); + nestingLevel: (int [@default 0]); + executionOrder: (int [@default 0]); + timestamp: (string [@default ""]); + importance: (stl_importance [@default Unimportant]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type threadFlow = { + id: (string [@default ""]); + message: (message [@default no_msg]); + locations: threadFlowLocation list; + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type attachment = { + description: (message [@default no_msg ]); + fileLocation: fileLocation; + regions: (region list [@default []]); + rectangles: (rectangle list [@default []]) +} [@@deriving yojson] + type codeFlow = { description: (message [@default no_msg]); threadFlows: threadFlow list; properties: (properties [@default no_prop]); } [@@deriving yojson] -type tool = { __toolname: string }[@@deriving yojson] +type sarif_exception = { + kind: (string [@default ""]); + message: (string [@default ""]); + stack: (stack [@default no_stack]); + innerExceptions: (sarif_exception list [@default []]); +}[@@deriving yojson] +let no_exn = { kind = ""; message = ""; stack = no_stack; innerExceptions = [] } + +type notification_kind = + | Note [@name "note"] + | Warning [@name "warning"] + | Error [@name "error"] +[@@deriving yojson] + +type notification = { + id: (string [@default ""]); + ruleId: (string [@default ""]); + physicalLocation: (physicalLocation [@default unknown_physicalLocation]); + message: message; + level: (notification_kind [@default Warning]); + threadId: (int [@default 0]); + time: (string [@default ""]); + exn: (sarif_exception [@default no_exn]) [@key "exception"]; + properties: (properties [@default no_prop]) +}[@@deriving yojson] + +type tool = { + name: string; + fullName: (string [@default ""]); + version: (string [@default ""]); + semanticVersion: (string [@default ""]); + fileVersion: (string [@default ""]); + downloadUri: (string [@default ""]); + sarifLoggerVersion: (string [@default ""]); + language: (string [@default "en-US"]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +let no_tool = { + name = ""; + fullName = ""; + version = ""; + semanticVersion = ""; + fileVersion = ""; + downloadUri = ""; + sarifLoggerVersion = ""; + language = ""; + properties = no_prop; +} + +type invocation = { + commandLine: string; + arguments: string list; + responseFiles: (fileLocation list [@default []]); + attachments: (attachment list [@default []]); + startTime: (string [@default ""]); + endTime: (string [@default ""]); + exitCode: int; + toolNotifications: (notification list [@default []]); + configurationNotifications: (notification list [@default []]); + exitCodeDescription: (string [@default ""]); + exitSignalName: (string [@default ""]); + exitSignalNumber: (int [@default 0]); + processStartFailureMessage: (string [@default ""]); + toolExecutionSuccessful: bool; + machine: (string [@default ""]); + account: (string [@default ""]); + processId: (int [@default 0]); + executableLocation: (fileLocation [@default unknown_file]); + workingDirectory: (fileLocation [@default unknown_file]); + environmentVariables: (additional_properties [@default no_additional_prop]); + stdin: (fileLocation [@default unknown_file]); + stdout: (fileLocation [@default unknown_file]); + stderr: (fileLocation [@default unknown_file]); + stdoutStderr: (fileLocation [@default unknown_file]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] -type invocation = { __cmdline: string list }[@@deriving yojson] -let std_invocation = { __cmdline = [] } +let std_invocation = { + commandLine = "/bin/cat"; + arguments = []; + responseFiles = []; + attachments = []; + startTime = ""; + endTime = ""; + exitCode = 0; + toolNotifications = []; + configurationNotifications = []; + exitCodeDescription = ""; + exitSignalName = ""; + exitSignalNumber = 0; + processStartFailureMessage = ""; + toolExecutionSuccessful = true; + machine = ""; + account = ""; + processId = 0; + executableLocation = unknown_file; + workingDirectory = unknown_file; + environmentVariables = no_additional_prop; + stdin = unknown_file; + stdout = unknown_file; + stderr = unknown_file; + stdoutStderr = unknown_file; + properties = no_prop; +} type conversion = { tool: tool; @@ -61,6 +296,12 @@ type conversion = { analysisToolLogFiles: (fileLocation [@default unknown_file]); } [@@deriving yojson] +let no_conversion = { + tool = no_tool; + invocation = std_invocation; + analysisToolLogFiles = unknown_file; +} + type edge = { id: string; label: (message [@default no_msg]); @@ -69,26 +310,89 @@ type edge = { properties: (properties [@default no_prop]) } [@@deriving yojson] -(* TODO: this type definition is unclear in the schema. *) -type finalState = { additionalProperties: string }[@@deriving yojson] - -let no_state_info = { additionalProperties = "" } +type node = + { id: string; + label: (string [@default ""]); + location: (location [@default no_loc]); + children: (node list [@default []]); + properties: (properties [@default no_prop]); + }[@@deriving yojson] type edge_traversal = { edgeId: string; message: (message [@default no_msg]); - finalState: (finalState [@default no_state_info]); + finalState: (additional_properties [@default no_additional_prop]); stepOverEdgeCount: (int [@default 0]); properties: (properties [@default no_prop]); }[@@deriving yojson] -type stack = { __stack: string list }[@@deriving yojson] +type role = + | AnalysisTarget [@name "analysisTarget"] + | Attachment [@name "attachment"] + | ResponseFile [@name "responseFile"] + | ResultFile [@name "resultFile"] + | StandardStrem [@name "standardStream"] + | TraceFile [@name "traceFile"] + | UnmodifiedFile [@name "unmodifiedFile"] + | ModifiedFile [@name "modifiedFile"] + | AddedFile [@name "addedFile"] + | DeletedFile [@name "deletedFile"] + | RenamedFile [@name "renamedFile"] + | UncontrolledFile [@name "uncontrolledFile"] +[@@deriving yojson] -type sarif_exception = { - kind: string; - message: string; - stack: stack; - innerExceptions: sarif_exception list +type hash = { + value: string; + algorithm: string +} [@@deriving yojson] + +type graph = { + id : string; + description: (message [@default no_msg]); + nodes: node list; + edges: edge list; + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type graph_dictionary = + [ `Null | `Assoc of (string * graph) list ][@@deriving yojson] +let no_graph = `Null + +type graphTraversal = { + graphId: string; + description: (message [@default no_msg]); + initialState: (additional_properties [@default no_additional_prop]); + edgeTraversals: edge_traversal list; + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type replacement = { + deletedRegion: region; + insertedContent: (fileContent [@default no_file_content]) +}[@@deriving yojson] + +type file = { + fileLocation: (fileLocation [@default unknown_file]); + parentKey: (string [@default ""]); + offset: (int [@default 0]); + length: (int [@default 0]); + roles: (role list [@default []]); + mimeType: (string [@default ""]); + contents: (fileContent [@default no_file_content]); + encoding: (string [@default ""]); + hashes: (hash list [@default []]); + lastModifiedTime: (string [@default ""]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +type fileChange = { + fileLocation: fileLocation; + replacements: replacement list +}[@@deriving yojson] + +type fix = { + description: (message [@defaut no_msg]); + fileChanges: fileChange list; }[@@deriving yojson] type externalFiles = { @@ -101,7 +405,156 @@ type externalFiles = { results: (fileLocation [@default unknown_file]); }[@@deriving yojson] -type run = Nothing +type logicalLocation = { + name: string; + fullyQualifiedName: string; + decoratedName: string; + parentKey: string; + kind: string; +}[@@deriving yojson] + +type ruleConfigLevel = + | Note [@name "note"] + | Warning [@name "warning"] + | Error [@name "error"] + | Open [@name "open"] +[@@deriving yojson] + +type ruleConfiguration = { + enabled: (bool [@default false]); + defaultLevel: (ruleConfigLevel [@default Open]); + parameters: (properties [@default no_prop]) +}[@@deriving yojson] + +let std_rule_config = { + enabled = false; + defaultLevel = Open; + parameters = no_prop; +} + +type rule = { + id: (string [@default ""]); + name: (string [@default ""]); + shortDescription: (message [@default no_msg]); + fullDescription: (message [@default no_msg]); + messageStrings: (additional_properties [@default no_additional_prop]); + richMessageStrings: (additional_properties [@default no_additional_prop]); + configuration: (ruleConfiguration [@default std_rule_config]); + helpUri: (string [@default ""]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +let no_rule = { + id = ""; + name = ""; + shortDescription = no_msg; + fullDescription = no_msg; + messageStrings = no_additional_prop; + richMessageStrings = no_additional_prop; + configuration = std_rule_config; + helpUri = ""; + properties = no_prop; +} + +type resources = { + messageStrings: (additional_properties [@default no_additional_prop]); + rules: (rule [@default no_rule]); +}[@@deriving yojson] + +let no_resources = { messageStrings = no_additional_prop; rules = no_rule } + +type result_level = + | NotApplicable [@name "notApplicable"] + | Pass [@name "pass"] + | Note [@name "note"] + | Warning [@name "warning"] + | Error [@name "error"] +[@@deriving yojson] + +type result_suppressionState = + | SuppressedInSource [@name "suppressedInSource"] + | SuppressedExternally [@name "suppressedExternally"] +[@@deriving yojson] + +type result_baselineState = + | New [@name "new"] + | Existing [@name "existing"] + | Absent [@name "absent"] +[@@deriving yojson] + +type result = { + ruleId: (string [@default ""]); + level: (result_level [@default NotApplicable]); + message: (message [@default no_msg]); + analysisTarget: (fileLocation [@default unknown_file]); + locations: (location list [@default []]); + instanceGuid: (string [@default ""]); + correlationGuid: (string [@default ""]); + occurenceCount: (int [@default 1]); + partialFingerprints: (additional_properties [@default no_additional_prop]); + fingerprints: (additional_properties [@default no_additional_prop]); + stacks: (stack list [@default []]); + codeFlows: (codeFlow list [@default []]); + graphs: (graph_dictionary [@default no_graph]); + graphTraversals: (graphTraversal list [@default []]); + relatedLocations: (location list [@default []]); + suppressionStates: (result_suppressionState list [@default []]); + baselineState: (result_baselineState [@default Absent]); + attachments: (attachment list [@default []]); + workItemsUris: (string list [@default []]); + conversionProvenance: (physicalLocation list [@default[]]); + fixes: (fix list [@default []]); + properties: (properties [@default no_prop]) +}[@@deriving yojson] + +type versionControlDetails = { + uri: string; + revisionId: (string [@default ""]); + branch: (string [@default ""]); + tag: (string [@default ""]); + timestamp: (string [@default ""]); + properties: (properties [@default no_prop]); +}[@@deriving yojson] + +(** TODO: `Assoc should take as argument a json rep of a file *) +type file_dictionary = + [ `Null | `Assoc of string * Yojson.Safe.json][@@deriving yojson] +let no_file = `Null + +(** TODO: values are logicalLocation *) +type logical_loc_dict = + [ `Null | `Assoc of string * logicalLocation][@@deriving yojson] +let no_location = `Null + +type columnKind = + | Utf16CodeUnits [@name "utf16CodeUnits"] + | UnicodeCodePoints [@name "unicodeCodePoints"] +[@@deriving yojson] + +type run = { + tool: tool; + invocations: (invocation list [@default []]); + conversion: (conversion [@default no_conversion]); + versionControlProvenance: (versionControlDetails list [@default []]); + originalUriBaseIds: (additional_properties [@default no_additional_prop]); + files: (file_dictionary [@default no_file]); + logicalLocations: (logical_loc_dict [@default no_location]); + graphs: (graph_dictionary [@default no_graph]); + results: (result list [@default []]); + resources: (resources [@default no_resources]); + instanceGuid: (string [@default ""]); + correlationGuid: (string [@default ""]); + logicalId: (string [@default ""]); + description: (message [@default no_msg]); + automationLogicalId: (string [@default ""]); + baselineInstanceGuid: (string [@default ""]); + architecture: (string [@default ""]); + richMessageMimeType: (string [@default "text/markdown;variant=GFM" ]); + redactionToken: (string [@default ""]); + defaultFileEncoding: (string [@default "utf-8"]); + columnKind: (columnKind [@default UnicodeCodePoints]); + properties: (properties [@default no_prop]); +} [@@deriving yojson] type schema = { -- GitLab