sarif-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/LICENSE +21 -0
  5. data/README.md +191 -0
  6. data/Rakefile +10 -0
  7. data/lib/sarif/address.rb +67 -0
  8. data/lib/sarif/artifact.rb +76 -0
  9. data/lib/sarif/artifact_change.rb +46 -0
  10. data/lib/sarif/artifact_content.rb +49 -0
  11. data/lib/sarif/artifact_location.rb +52 -0
  12. data/lib/sarif/attachment.rb +52 -0
  13. data/lib/sarif/code_flow.rb +46 -0
  14. data/lib/sarif/configuration_override.rb +46 -0
  15. data/lib/sarif/conversion.rb +49 -0
  16. data/lib/sarif/edge.rb +52 -0
  17. data/lib/sarif/edge_traversal.rb +52 -0
  18. data/lib/sarif/exception.rb +52 -0
  19. data/lib/sarif/external_properties.rb +100 -0
  20. data/lib/sarif/external_property_file_reference.rb +49 -0
  21. data/lib/sarif/external_property_file_references.rb +88 -0
  22. data/lib/sarif/fix.rb +46 -0
  23. data/lib/sarif/graph.rb +49 -0
  24. data/lib/sarif/graph_traversal.rb +58 -0
  25. data/lib/sarif/invocation.rb +115 -0
  26. data/lib/sarif/location.rb +58 -0
  27. data/lib/sarif/location_relationship.rb +49 -0
  28. data/lib/sarif/log.rb +52 -0
  29. data/lib/sarif/logical_location.rb +58 -0
  30. data/lib/sarif/message.rb +52 -0
  31. data/lib/sarif/multiformat_message_string.rb +46 -0
  32. data/lib/sarif/node.rb +52 -0
  33. data/lib/sarif/notification.rb +64 -0
  34. data/lib/sarif/physical_location.rb +52 -0
  35. data/lib/sarif/property_bag.rb +40 -0
  36. data/lib/sarif/rectangle.rb +55 -0
  37. data/lib/sarif/region.rb +73 -0
  38. data/lib/sarif/replacement.rb +46 -0
  39. data/lib/sarif/reporting_configuration.rb +52 -0
  40. data/lib/sarif/reporting_descriptor.rb +79 -0
  41. data/lib/sarif/reporting_descriptor_reference.rb +52 -0
  42. data/lib/sarif/reporting_descriptor_relationship.rb +49 -0
  43. data/lib/sarif/result.rb +127 -0
  44. data/lib/sarif/result_provenance.rb +58 -0
  45. data/lib/sarif/run.rb +121 -0
  46. data/lib/sarif/run_automation_details.rb +52 -0
  47. data/lib/sarif/schema/sarif-schema-2.1.0.json +3389 -0
  48. data/lib/sarif/special_locations.rb +43 -0
  49. data/lib/sarif/stack.rb +46 -0
  50. data/lib/sarif/stack_frame.rb +52 -0
  51. data/lib/sarif/suppression.rb +55 -0
  52. data/lib/sarif/thread_flow.rb +55 -0
  53. data/lib/sarif/thread_flow_location.rb +79 -0
  54. data/lib/sarif/tool.rb +46 -0
  55. data/lib/sarif/tool_component.rb +121 -0
  56. data/lib/sarif/tool_component_reference.rb +49 -0
  57. data/lib/sarif/translation_metadata.rb +58 -0
  58. data/lib/sarif/version.rb +5 -0
  59. data/lib/sarif/version_control_details.rb +58 -0
  60. data/lib/sarif/web_request.rb +64 -0
  61. data/lib/sarif/web_response.rb +64 -0
  62. data/lib/sarif.rb +121 -0
  63. data/sig/sarif.rbs +4 -0
  64. metadata +106 -0
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # An area within an image.
5
+ class Rectangle
6
+ attr_accessor :top, :left, :bottom, :right, :message, :properties
7
+
8
+ def initialize(top: nil, left: nil, bottom: nil, right: nil, message: nil, properties: nil)
9
+ @top = top
10
+ @left = left
11
+ @bottom = bottom
12
+ @right = right
13
+ @message = message
14
+ @properties = properties
15
+ end
16
+
17
+ def to_h
18
+ h = {}
19
+ h["top"] = @top unless @top.nil?
20
+ h["left"] = @left unless @left.nil?
21
+ h["bottom"] = @bottom unless @bottom.nil?
22
+ h["right"] = @right unless @right.nil?
23
+ h["message"] = @message&.to_h unless @message.nil?
24
+ h["properties"] = @properties unless @properties.nil?
25
+ h
26
+ end
27
+
28
+ def to_json(pretty: false)
29
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
30
+ end
31
+
32
+ def self.from_hash(h)
33
+ return nil if h.nil?
34
+ new(
35
+ top: h["top"],
36
+ left: h["left"],
37
+ bottom: h["bottom"],
38
+ right: h["right"],
39
+ message: Message.from_hash(h["message"]),
40
+ properties: h["properties"]
41
+ )
42
+ end
43
+
44
+ def ==(other)
45
+ return false unless other.is_a?(Rectangle)
46
+ @top == other.top && @left == other.left && @bottom == other.bottom && @right == other.right && @message == other.message && @properties == other.properties
47
+ end
48
+
49
+ alias eql? ==
50
+
51
+ def hash
52
+ [@top, @left, @bottom, @right, @message, @properties].hash
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A region within an artifact where a result was detected.
5
+ class Region
6
+ attr_accessor :start_line, :start_column, :end_line, :end_column, :char_offset, :char_length, :byte_offset, :byte_length, :snippet, :message, :source_language, :properties
7
+
8
+ def initialize(start_line: nil, start_column: nil, end_line: nil, end_column: nil, char_offset: -1, char_length: nil, byte_offset: -1, byte_length: nil, snippet: nil, message: nil, source_language: nil, properties: nil)
9
+ @start_line = start_line
10
+ @start_column = start_column
11
+ @end_line = end_line
12
+ @end_column = end_column
13
+ @char_offset = char_offset
14
+ @char_length = char_length
15
+ @byte_offset = byte_offset
16
+ @byte_length = byte_length
17
+ @snippet = snippet
18
+ @message = message
19
+ @source_language = source_language
20
+ @properties = properties
21
+ end
22
+
23
+ def to_h
24
+ h = {}
25
+ h["startLine"] = @start_line unless @start_line.nil?
26
+ h["startColumn"] = @start_column unless @start_column.nil?
27
+ h["endLine"] = @end_line unless @end_line.nil?
28
+ h["endColumn"] = @end_column unless @end_column.nil?
29
+ h["charOffset"] = @char_offset if @char_offset && @char_offset != -1
30
+ h["charLength"] = @char_length unless @char_length.nil?
31
+ h["byteOffset"] = @byte_offset if @byte_offset && @byte_offset != -1
32
+ h["byteLength"] = @byte_length unless @byte_length.nil?
33
+ h["snippet"] = @snippet&.to_h unless @snippet.nil?
34
+ h["message"] = @message&.to_h unless @message.nil?
35
+ h["sourceLanguage"] = @source_language unless @source_language.nil?
36
+ h["properties"] = @properties unless @properties.nil?
37
+ h
38
+ end
39
+
40
+ def to_json(pretty: false)
41
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
42
+ end
43
+
44
+ def self.from_hash(h)
45
+ return nil if h.nil?
46
+ new(
47
+ start_line: h["startLine"],
48
+ start_column: h["startColumn"],
49
+ end_line: h["endLine"],
50
+ end_column: h["endColumn"],
51
+ char_offset: h["charOffset"] || -1,
52
+ char_length: h["charLength"],
53
+ byte_offset: h["byteOffset"] || -1,
54
+ byte_length: h["byteLength"],
55
+ snippet: ArtifactContent.from_hash(h["snippet"]),
56
+ message: Message.from_hash(h["message"]),
57
+ source_language: h["sourceLanguage"],
58
+ properties: h["properties"]
59
+ )
60
+ end
61
+
62
+ def ==(other)
63
+ return false unless other.is_a?(Region)
64
+ @start_line == other.start_line && @start_column == other.start_column && @end_line == other.end_line && @end_column == other.end_column && @char_offset == other.char_offset && @char_length == other.char_length && @byte_offset == other.byte_offset && @byte_length == other.byte_length && @snippet == other.snippet && @message == other.message && @source_language == other.source_language && @properties == other.properties
65
+ end
66
+
67
+ alias eql? ==
68
+
69
+ def hash
70
+ [@start_line, @start_column, @end_line, @end_column, @char_offset, @char_length, @byte_offset, @byte_length, @snippet, @message, @source_language, @properties].hash
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # The replacement of a single region of an artifact.
5
+ class Replacement
6
+ attr_accessor :deleted_region, :inserted_content, :properties
7
+
8
+ def initialize(deleted_region:, inserted_content: nil, properties: nil)
9
+ @deleted_region = deleted_region
10
+ @inserted_content = inserted_content
11
+ @properties = properties
12
+ end
13
+
14
+ def to_h
15
+ h = {}
16
+ h["deletedRegion"] = @deleted_region&.to_h
17
+ h["insertedContent"] = @inserted_content&.to_h unless @inserted_content.nil?
18
+ h["properties"] = @properties unless @properties.nil?
19
+ h
20
+ end
21
+
22
+ def to_json(pretty: false)
23
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
24
+ end
25
+
26
+ def self.from_hash(h)
27
+ return nil if h.nil?
28
+ new(
29
+ deleted_region: Region.from_hash(h["deletedRegion"]),
30
+ inserted_content: ArtifactContent.from_hash(h["insertedContent"]),
31
+ properties: h["properties"]
32
+ )
33
+ end
34
+
35
+ def ==(other)
36
+ return false unless other.is_a?(Replacement)
37
+ @deleted_region == other.deleted_region && @inserted_content == other.inserted_content && @properties == other.properties
38
+ end
39
+
40
+ alias eql? ==
41
+
42
+ def hash
43
+ [@deleted_region, @inserted_content, @properties].hash
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Information about a rule or notification that can be configured at runtime.
5
+ class ReportingConfiguration
6
+ attr_accessor :enabled, :level, :rank, :parameters, :properties
7
+
8
+ def initialize(enabled: true, level: "warning", rank: -1.0, parameters: nil, properties: nil)
9
+ @enabled = enabled
10
+ @level = level
11
+ @rank = rank
12
+ @parameters = parameters
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["enabled"] = @enabled unless @enabled
19
+ h["level"] = @level&.to_s if @level && @level != "warning"
20
+ h["rank"] = @rank if @rank && @rank != -1
21
+ h["parameters"] = @parameters unless @parameters.nil?
22
+ h["properties"] = @properties unless @properties.nil?
23
+ h
24
+ end
25
+
26
+ def to_json(pretty: false)
27
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
28
+ end
29
+
30
+ def self.from_hash(h)
31
+ return nil if h.nil?
32
+ new(
33
+ enabled: h.key?("enabled") ? h["enabled"] : true,
34
+ level: h["level"] || "warning",
35
+ rank: h["rank"] || -1.0,
36
+ parameters: h["parameters"],
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(ReportingConfiguration)
43
+ @enabled == other.enabled && @level == other.level && @rank == other.rank && @parameters == other.parameters && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@enabled, @level, @rank, @parameters, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Metadata that describes a specific report produced by the tool, as part of the analysis it provides or its runtime reporting.
5
+ class ReportingDescriptor
6
+ attr_accessor :id, :deprecated_ids, :guid, :deprecated_guids, :name, :deprecated_names, :short_description, :full_description, :message_strings, :default_configuration, :help_uri, :help, :relationships, :properties
7
+
8
+ def initialize(id:, deprecated_ids: nil, guid: nil, deprecated_guids: nil, name: nil, deprecated_names: nil, short_description: nil, full_description: nil, message_strings: nil, default_configuration: nil, help_uri: nil, help: nil, relationships: [], properties: nil)
9
+ @id = id
10
+ @deprecated_ids = deprecated_ids
11
+ @guid = guid
12
+ @deprecated_guids = deprecated_guids
13
+ @name = name
14
+ @deprecated_names = deprecated_names
15
+ @short_description = short_description
16
+ @full_description = full_description
17
+ @message_strings = message_strings
18
+ @default_configuration = default_configuration
19
+ @help_uri = help_uri
20
+ @help = help
21
+ @relationships = relationships
22
+ @properties = properties
23
+ end
24
+
25
+ def to_h
26
+ h = {}
27
+ h["id"] = @id
28
+ h["deprecatedIds"] = @deprecated_ids if @deprecated_ids&.any?
29
+ h["guid"] = @guid unless @guid.nil?
30
+ h["deprecatedGuids"] = @deprecated_guids if @deprecated_guids&.any?
31
+ h["name"] = @name unless @name.nil?
32
+ h["deprecatedNames"] = @deprecated_names if @deprecated_names&.any?
33
+ h["shortDescription"] = @short_description&.to_h unless @short_description.nil?
34
+ h["fullDescription"] = @full_description&.to_h unless @full_description.nil?
35
+ h["messageStrings"] = @message_strings unless @message_strings.nil?
36
+ h["defaultConfiguration"] = @default_configuration&.to_h unless @default_configuration.nil?
37
+ h["helpUri"] = @help_uri unless @help_uri.nil?
38
+ h["help"] = @help&.to_h unless @help.nil?
39
+ h["relationships"] = @relationships&.map(&:to_h) if @relationships&.any?
40
+ h["properties"] = @properties unless @properties.nil?
41
+ h
42
+ end
43
+
44
+ def to_json(pretty: false)
45
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
46
+ end
47
+
48
+ def self.from_hash(h)
49
+ return nil if h.nil?
50
+ new(
51
+ id: h["id"],
52
+ deprecated_ids: h["deprecatedIds"],
53
+ guid: h["guid"],
54
+ deprecated_guids: h["deprecatedGuids"],
55
+ name: h["name"],
56
+ deprecated_names: h["deprecatedNames"],
57
+ short_description: MultiformatMessageString.from_hash(h["shortDescription"]),
58
+ full_description: MultiformatMessageString.from_hash(h["fullDescription"]),
59
+ message_strings: h["messageStrings"],
60
+ default_configuration: ReportingConfiguration.from_hash(h["defaultConfiguration"]),
61
+ help_uri: h["helpUri"],
62
+ help: MultiformatMessageString.from_hash(h["help"]),
63
+ relationships: h["relationships"]&.map { |v| ReportingDescriptorRelationship.from_hash(v) } || [],
64
+ properties: h["properties"]
65
+ )
66
+ end
67
+
68
+ def ==(other)
69
+ return false unless other.is_a?(ReportingDescriptor)
70
+ @id == other.id && @deprecated_ids == other.deprecated_ids && @guid == other.guid && @deprecated_guids == other.deprecated_guids && @name == other.name && @deprecated_names == other.deprecated_names && @short_description == other.short_description && @full_description == other.full_description && @message_strings == other.message_strings && @default_configuration == other.default_configuration && @help_uri == other.help_uri && @help == other.help && @relationships == other.relationships && @properties == other.properties
71
+ end
72
+
73
+ alias eql? ==
74
+
75
+ def hash
76
+ [@id, @deprecated_ids, @guid, @deprecated_guids, @name, @deprecated_names, @short_description, @full_description, @message_strings, @default_configuration, @help_uri, @help, @relationships, @properties].hash
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Information about how to locate a relevant reporting descriptor.
5
+ class ReportingDescriptorReference
6
+ attr_accessor :id, :index, :guid, :tool_component, :properties
7
+
8
+ def initialize(id: nil, index: -1, guid: nil, tool_component: nil, properties: nil)
9
+ @id = id
10
+ @index = index
11
+ @guid = guid
12
+ @tool_component = tool_component
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["id"] = @id unless @id.nil?
19
+ h["index"] = @index if @index && @index != -1
20
+ h["guid"] = @guid unless @guid.nil?
21
+ h["toolComponent"] = @tool_component&.to_h unless @tool_component.nil?
22
+ h["properties"] = @properties unless @properties.nil?
23
+ h
24
+ end
25
+
26
+ def to_json(pretty: false)
27
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
28
+ end
29
+
30
+ def self.from_hash(h)
31
+ return nil if h.nil?
32
+ new(
33
+ id: h["id"],
34
+ index: h["index"] || -1,
35
+ guid: h["guid"],
36
+ tool_component: ToolComponentReference.from_hash(h["toolComponent"]),
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(ReportingDescriptorReference)
43
+ @id == other.id && @index == other.index && @guid == other.guid && @tool_component == other.tool_component && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@id, @index, @guid, @tool_component, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Information about the relation of one reporting descriptor to another.
5
+ class ReportingDescriptorRelationship
6
+ attr_accessor :target, :kinds, :description, :properties
7
+
8
+ def initialize(target:, kinds: ["relevant"], description: nil, properties: nil)
9
+ @target = target
10
+ @kinds = kinds
11
+ @description = description
12
+ @properties = properties
13
+ end
14
+
15
+ def to_h
16
+ h = {}
17
+ h["target"] = @target&.to_h
18
+ h["kinds"] = @kinds if @kinds&.any?
19
+ h["description"] = @description&.to_h unless @description.nil?
20
+ h["properties"] = @properties unless @properties.nil?
21
+ h
22
+ end
23
+
24
+ def to_json(pretty: false)
25
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
26
+ end
27
+
28
+ def self.from_hash(h)
29
+ return nil if h.nil?
30
+ new(
31
+ target: ReportingDescriptorReference.from_hash(h["target"]),
32
+ kinds: h.key?("kinds") ? h["kinds"] : ["relevant"],
33
+ description: Message.from_hash(h["description"]),
34
+ properties: h["properties"]
35
+ )
36
+ end
37
+
38
+ def ==(other)
39
+ return false unless other.is_a?(ReportingDescriptorRelationship)
40
+ @target == other.target && @kinds == other.kinds && @description == other.description && @properties == other.properties
41
+ end
42
+
43
+ alias eql? ==
44
+
45
+ def hash
46
+ [@target, @kinds, @description, @properties].hash
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A result produced by an analysis tool.
5
+ class Result
6
+ attr_accessor :rule_id, :rule_index, :rule, :kind, :level, :message, :analysis_target, :locations, :guid, :correlation_guid, :occurrence_count, :partial_fingerprints, :fingerprints, :stacks, :code_flows, :graphs, :graph_traversals, :related_locations, :suppressions, :baseline_state, :rank, :attachments, :hosted_viewer_uri, :work_item_uris, :provenance, :fixes, :taxa, :web_request, :web_response, :properties
7
+
8
+ def initialize(rule_id: nil, rule_index: -1, rule: nil, kind: "fail", level: "warning", message:, analysis_target: nil, locations: [], guid: nil, correlation_guid: nil, occurrence_count: nil, partial_fingerprints: nil, fingerprints: nil, stacks: [], code_flows: [], graphs: [], graph_traversals: [], related_locations: [], suppressions: nil, baseline_state: nil, rank: -1.0, attachments: [], hosted_viewer_uri: nil, work_item_uris: nil, provenance: nil, fixes: [], taxa: [], web_request: nil, web_response: nil, properties: nil)
9
+ @rule_id = rule_id
10
+ @rule_index = rule_index
11
+ @rule = rule
12
+ @kind = kind
13
+ @level = level
14
+ @message = message
15
+ @analysis_target = analysis_target
16
+ @locations = locations
17
+ @guid = guid
18
+ @correlation_guid = correlation_guid
19
+ @occurrence_count = occurrence_count
20
+ @partial_fingerprints = partial_fingerprints
21
+ @fingerprints = fingerprints
22
+ @stacks = stacks
23
+ @code_flows = code_flows
24
+ @graphs = graphs
25
+ @graph_traversals = graph_traversals
26
+ @related_locations = related_locations
27
+ @suppressions = suppressions
28
+ @baseline_state = baseline_state
29
+ @rank = rank
30
+ @attachments = attachments
31
+ @hosted_viewer_uri = hosted_viewer_uri
32
+ @work_item_uris = work_item_uris
33
+ @provenance = provenance
34
+ @fixes = fixes
35
+ @taxa = taxa
36
+ @web_request = web_request
37
+ @web_response = web_response
38
+ @properties = properties
39
+ end
40
+
41
+ def to_h
42
+ h = {}
43
+ h["ruleId"] = @rule_id unless @rule_id.nil?
44
+ h["ruleIndex"] = @rule_index if @rule_index && @rule_index != -1
45
+ h["rule"] = @rule&.to_h unless @rule.nil?
46
+ h["kind"] = @kind&.to_s if @kind && @kind != "fail"
47
+ h["level"] = @level&.to_s if @level && @level != "warning"
48
+ h["message"] = @message&.to_h
49
+ h["analysisTarget"] = @analysis_target&.to_h unless @analysis_target.nil?
50
+ h["locations"] = @locations&.map(&:to_h) if @locations&.any?
51
+ h["guid"] = @guid unless @guid.nil?
52
+ h["correlationGuid"] = @correlation_guid unless @correlation_guid.nil?
53
+ h["occurrenceCount"] = @occurrence_count unless @occurrence_count.nil?
54
+ h["partialFingerprints"] = @partial_fingerprints unless @partial_fingerprints.nil?
55
+ h["fingerprints"] = @fingerprints unless @fingerprints.nil?
56
+ h["stacks"] = @stacks&.map(&:to_h) if @stacks&.any?
57
+ h["codeFlows"] = @code_flows&.map(&:to_h) if @code_flows&.any?
58
+ h["graphs"] = @graphs&.map(&:to_h) if @graphs&.any?
59
+ h["graphTraversals"] = @graph_traversals&.map(&:to_h) if @graph_traversals&.any?
60
+ h["relatedLocations"] = @related_locations&.map(&:to_h) if @related_locations&.any?
61
+ h["suppressions"] = @suppressions&.map(&:to_h) if @suppressions&.any?
62
+ h["baselineState"] = @baseline_state&.to_s unless @baseline_state.nil?
63
+ h["rank"] = @rank if @rank && @rank != -1
64
+ h["attachments"] = @attachments&.map(&:to_h) if @attachments&.any?
65
+ h["hostedViewerUri"] = @hosted_viewer_uri unless @hosted_viewer_uri.nil?
66
+ h["workItemUris"] = @work_item_uris if @work_item_uris&.any?
67
+ h["provenance"] = @provenance&.to_h unless @provenance.nil?
68
+ h["fixes"] = @fixes&.map(&:to_h) if @fixes&.any?
69
+ h["taxa"] = @taxa&.map(&:to_h) if @taxa&.any?
70
+ h["webRequest"] = @web_request&.to_h unless @web_request.nil?
71
+ h["webResponse"] = @web_response&.to_h unless @web_response.nil?
72
+ h["properties"] = @properties unless @properties.nil?
73
+ h
74
+ end
75
+
76
+ def to_json(pretty: false)
77
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
78
+ end
79
+
80
+ def self.from_hash(h)
81
+ return nil if h.nil?
82
+ new(
83
+ rule_id: h["ruleId"],
84
+ rule_index: h["ruleIndex"] || -1,
85
+ rule: ReportingDescriptorReference.from_hash(h["rule"]),
86
+ kind: h["kind"] || "fail",
87
+ level: h["level"] || "warning",
88
+ message: Message.from_hash(h["message"]),
89
+ analysis_target: ArtifactLocation.from_hash(h["analysisTarget"]),
90
+ locations: h["locations"]&.map { |v| Location.from_hash(v) } || [],
91
+ guid: h["guid"],
92
+ correlation_guid: h["correlationGuid"],
93
+ occurrence_count: h["occurrenceCount"],
94
+ partial_fingerprints: h["partialFingerprints"],
95
+ fingerprints: h["fingerprints"],
96
+ stacks: h["stacks"]&.map { |v| Stack.from_hash(v) } || [],
97
+ code_flows: h["codeFlows"]&.map { |v| CodeFlow.from_hash(v) } || [],
98
+ graphs: h["graphs"]&.map { |v| Graph.from_hash(v) } || [],
99
+ graph_traversals: h["graphTraversals"]&.map { |v| GraphTraversal.from_hash(v) } || [],
100
+ related_locations: h["relatedLocations"]&.map { |v| Location.from_hash(v) } || [],
101
+ suppressions: h["suppressions"]&.map { |v| Suppression.from_hash(v) },
102
+ baseline_state: h["baselineState"],
103
+ rank: h["rank"] || -1.0,
104
+ attachments: h["attachments"]&.map { |v| Attachment.from_hash(v) } || [],
105
+ hosted_viewer_uri: h["hostedViewerUri"],
106
+ work_item_uris: h["workItemUris"],
107
+ provenance: ResultProvenance.from_hash(h["provenance"]),
108
+ fixes: h["fixes"]&.map { |v| Fix.from_hash(v) } || [],
109
+ taxa: h["taxa"]&.map { |v| ReportingDescriptorReference.from_hash(v) } || [],
110
+ web_request: WebRequest.from_hash(h["webRequest"]),
111
+ web_response: WebResponse.from_hash(h["webResponse"]),
112
+ properties: h["properties"]
113
+ )
114
+ end
115
+
116
+ def ==(other)
117
+ return false unless other.is_a?(Result)
118
+ @rule_id == other.rule_id && @rule_index == other.rule_index && @rule == other.rule && @kind == other.kind && @level == other.level && @message == other.message && @analysis_target == other.analysis_target && @locations == other.locations && @guid == other.guid && @correlation_guid == other.correlation_guid && @occurrence_count == other.occurrence_count && @partial_fingerprints == other.partial_fingerprints && @fingerprints == other.fingerprints && @stacks == other.stacks && @code_flows == other.code_flows && @graphs == other.graphs && @graph_traversals == other.graph_traversals && @related_locations == other.related_locations && @suppressions == other.suppressions && @baseline_state == other.baseline_state && @rank == other.rank && @attachments == other.attachments && @hosted_viewer_uri == other.hosted_viewer_uri && @work_item_uris == other.work_item_uris && @provenance == other.provenance && @fixes == other.fixes && @taxa == other.taxa && @web_request == other.web_request && @web_response == other.web_response && @properties == other.properties
119
+ end
120
+
121
+ alias eql? ==
122
+
123
+ def hash
124
+ [@rule_id, @rule_index, @rule, @kind, @level, @message, @analysis_target, @locations, @guid, @correlation_guid, @occurrence_count, @partial_fingerprints, @fingerprints, @stacks, @code_flows, @graphs, @graph_traversals, @related_locations, @suppressions, @baseline_state, @rank, @attachments, @hosted_viewer_uri, @work_item_uris, @provenance, @fixes, @taxa, @web_request, @web_response, @properties].hash
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Contains information about how and when a result was detected.
5
+ class ResultProvenance
6
+ attr_accessor :first_detection_time_utc, :last_detection_time_utc, :first_detection_run_guid, :last_detection_run_guid, :invocation_index, :conversion_sources, :properties
7
+
8
+ def initialize(first_detection_time_utc: nil, last_detection_time_utc: nil, first_detection_run_guid: nil, last_detection_run_guid: nil, invocation_index: -1, conversion_sources: [], properties: nil)
9
+ @first_detection_time_utc = first_detection_time_utc
10
+ @last_detection_time_utc = last_detection_time_utc
11
+ @first_detection_run_guid = first_detection_run_guid
12
+ @last_detection_run_guid = last_detection_run_guid
13
+ @invocation_index = invocation_index
14
+ @conversion_sources = conversion_sources
15
+ @properties = properties
16
+ end
17
+
18
+ def to_h
19
+ h = {}
20
+ h["firstDetectionTimeUtc"] = @first_detection_time_utc unless @first_detection_time_utc.nil?
21
+ h["lastDetectionTimeUtc"] = @last_detection_time_utc unless @last_detection_time_utc.nil?
22
+ h["firstDetectionRunGuid"] = @first_detection_run_guid unless @first_detection_run_guid.nil?
23
+ h["lastDetectionRunGuid"] = @last_detection_run_guid unless @last_detection_run_guid.nil?
24
+ h["invocationIndex"] = @invocation_index if @invocation_index && @invocation_index != -1
25
+ h["conversionSources"] = @conversion_sources&.map(&:to_h) if @conversion_sources&.any?
26
+ h["properties"] = @properties unless @properties.nil?
27
+ h
28
+ end
29
+
30
+ def to_json(pretty: false)
31
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
32
+ end
33
+
34
+ def self.from_hash(h)
35
+ return nil if h.nil?
36
+ new(
37
+ first_detection_time_utc: h["firstDetectionTimeUtc"],
38
+ last_detection_time_utc: h["lastDetectionTimeUtc"],
39
+ first_detection_run_guid: h["firstDetectionRunGuid"],
40
+ last_detection_run_guid: h["lastDetectionRunGuid"],
41
+ invocation_index: h["invocationIndex"] || -1,
42
+ conversion_sources: h["conversionSources"]&.map { |v| PhysicalLocation.from_hash(v) } || [],
43
+ properties: h["properties"]
44
+ )
45
+ end
46
+
47
+ def ==(other)
48
+ return false unless other.is_a?(ResultProvenance)
49
+ @first_detection_time_utc == other.first_detection_time_utc && @last_detection_time_utc == other.last_detection_time_utc && @first_detection_run_guid == other.first_detection_run_guid && @last_detection_run_guid == other.last_detection_run_guid && @invocation_index == other.invocation_index && @conversion_sources == other.conversion_sources && @properties == other.properties
50
+ end
51
+
52
+ alias eql? ==
53
+
54
+ def hash
55
+ [@first_detection_time_utc, @last_detection_time_utc, @first_detection_run_guid, @last_detection_run_guid, @invocation_index, @conversion_sources, @properties].hash
56
+ end
57
+ end
58
+ end