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,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # The runtime environment of the analysis tool run.
5
+ class Invocation
6
+ attr_accessor :command_line, :arguments, :response_files, :start_time_utc, :end_time_utc, :exit_code, :rule_configuration_overrides, :notification_configuration_overrides, :tool_execution_notifications, :tool_configuration_notifications, :exit_code_description, :exit_signal_name, :exit_signal_number, :process_start_failure_message, :execution_successful, :machine, :account, :process_id, :executable_location, :working_directory, :environment_variables, :stdin, :stdout, :stderr, :stdout_stderr, :properties
7
+
8
+ def initialize(command_line: nil, arguments: nil, response_files: nil, start_time_utc: nil, end_time_utc: nil, exit_code: nil, rule_configuration_overrides: [], notification_configuration_overrides: [], tool_execution_notifications: [], tool_configuration_notifications: [], exit_code_description: nil, exit_signal_name: nil, exit_signal_number: nil, process_start_failure_message: nil, execution_successful:, machine: nil, account: nil, process_id: nil, executable_location: nil, working_directory: nil, environment_variables: nil, stdin: nil, stdout: nil, stderr: nil, stdout_stderr: nil, properties: nil)
9
+ @command_line = command_line
10
+ @arguments = arguments
11
+ @response_files = response_files
12
+ @start_time_utc = start_time_utc
13
+ @end_time_utc = end_time_utc
14
+ @exit_code = exit_code
15
+ @rule_configuration_overrides = rule_configuration_overrides
16
+ @notification_configuration_overrides = notification_configuration_overrides
17
+ @tool_execution_notifications = tool_execution_notifications
18
+ @tool_configuration_notifications = tool_configuration_notifications
19
+ @exit_code_description = exit_code_description
20
+ @exit_signal_name = exit_signal_name
21
+ @exit_signal_number = exit_signal_number
22
+ @process_start_failure_message = process_start_failure_message
23
+ @execution_successful = execution_successful
24
+ @machine = machine
25
+ @account = account
26
+ @process_id = process_id
27
+ @executable_location = executable_location
28
+ @working_directory = working_directory
29
+ @environment_variables = environment_variables
30
+ @stdin = stdin
31
+ @stdout = stdout
32
+ @stderr = stderr
33
+ @stdout_stderr = stdout_stderr
34
+ @properties = properties
35
+ end
36
+
37
+ def to_h
38
+ h = {}
39
+ h["commandLine"] = @command_line unless @command_line.nil?
40
+ h["arguments"] = @arguments if @arguments&.any?
41
+ h["responseFiles"] = @response_files&.map(&:to_h) if @response_files&.any?
42
+ h["startTimeUtc"] = @start_time_utc unless @start_time_utc.nil?
43
+ h["endTimeUtc"] = @end_time_utc unless @end_time_utc.nil?
44
+ h["exitCode"] = @exit_code unless @exit_code.nil?
45
+ h["ruleConfigurationOverrides"] = @rule_configuration_overrides&.map(&:to_h) if @rule_configuration_overrides&.any?
46
+ h["notificationConfigurationOverrides"] = @notification_configuration_overrides&.map(&:to_h) if @notification_configuration_overrides&.any?
47
+ h["toolExecutionNotifications"] = @tool_execution_notifications&.map(&:to_h) if @tool_execution_notifications&.any?
48
+ h["toolConfigurationNotifications"] = @tool_configuration_notifications&.map(&:to_h) if @tool_configuration_notifications&.any?
49
+ h["exitCodeDescription"] = @exit_code_description unless @exit_code_description.nil?
50
+ h["exitSignalName"] = @exit_signal_name unless @exit_signal_name.nil?
51
+ h["exitSignalNumber"] = @exit_signal_number unless @exit_signal_number.nil?
52
+ h["processStartFailureMessage"] = @process_start_failure_message unless @process_start_failure_message.nil?
53
+ h["executionSuccessful"] = @execution_successful
54
+ h["machine"] = @machine unless @machine.nil?
55
+ h["account"] = @account unless @account.nil?
56
+ h["processId"] = @process_id unless @process_id.nil?
57
+ h["executableLocation"] = @executable_location&.to_h unless @executable_location.nil?
58
+ h["workingDirectory"] = @working_directory&.to_h unless @working_directory.nil?
59
+ h["environmentVariables"] = @environment_variables unless @environment_variables.nil?
60
+ h["stdin"] = @stdin&.to_h unless @stdin.nil?
61
+ h["stdout"] = @stdout&.to_h unless @stdout.nil?
62
+ h["stderr"] = @stderr&.to_h unless @stderr.nil?
63
+ h["stdoutStderr"] = @stdout_stderr&.to_h unless @stdout_stderr.nil?
64
+ h["properties"] = @properties unless @properties.nil?
65
+ h
66
+ end
67
+
68
+ def to_json(pretty: false)
69
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
70
+ end
71
+
72
+ def self.from_hash(h)
73
+ return nil if h.nil?
74
+ new(
75
+ command_line: h["commandLine"],
76
+ arguments: h["arguments"],
77
+ response_files: h["responseFiles"]&.map { |v| ArtifactLocation.from_hash(v) },
78
+ start_time_utc: h["startTimeUtc"],
79
+ end_time_utc: h["endTimeUtc"],
80
+ exit_code: h["exitCode"],
81
+ rule_configuration_overrides: h["ruleConfigurationOverrides"]&.map { |v| ConfigurationOverride.from_hash(v) } || [],
82
+ notification_configuration_overrides: h["notificationConfigurationOverrides"]&.map { |v| ConfigurationOverride.from_hash(v) } || [],
83
+ tool_execution_notifications: h["toolExecutionNotifications"]&.map { |v| Notification.from_hash(v) } || [],
84
+ tool_configuration_notifications: h["toolConfigurationNotifications"]&.map { |v| Notification.from_hash(v) } || [],
85
+ exit_code_description: h["exitCodeDescription"],
86
+ exit_signal_name: h["exitSignalName"],
87
+ exit_signal_number: h["exitSignalNumber"],
88
+ process_start_failure_message: h["processStartFailureMessage"],
89
+ execution_successful: h["executionSuccessful"],
90
+ machine: h["machine"],
91
+ account: h["account"],
92
+ process_id: h["processId"],
93
+ executable_location: ArtifactLocation.from_hash(h["executableLocation"]),
94
+ working_directory: ArtifactLocation.from_hash(h["workingDirectory"]),
95
+ environment_variables: h["environmentVariables"],
96
+ stdin: ArtifactLocation.from_hash(h["stdin"]),
97
+ stdout: ArtifactLocation.from_hash(h["stdout"]),
98
+ stderr: ArtifactLocation.from_hash(h["stderr"]),
99
+ stdout_stderr: ArtifactLocation.from_hash(h["stdoutStderr"]),
100
+ properties: h["properties"]
101
+ )
102
+ end
103
+
104
+ def ==(other)
105
+ return false unless other.is_a?(Invocation)
106
+ @command_line == other.command_line && @arguments == other.arguments && @response_files == other.response_files && @start_time_utc == other.start_time_utc && @end_time_utc == other.end_time_utc && @exit_code == other.exit_code && @rule_configuration_overrides == other.rule_configuration_overrides && @notification_configuration_overrides == other.notification_configuration_overrides && @tool_execution_notifications == other.tool_execution_notifications && @tool_configuration_notifications == other.tool_configuration_notifications && @exit_code_description == other.exit_code_description && @exit_signal_name == other.exit_signal_name && @exit_signal_number == other.exit_signal_number && @process_start_failure_message == other.process_start_failure_message && @execution_successful == other.execution_successful && @machine == other.machine && @account == other.account && @process_id == other.process_id && @executable_location == other.executable_location && @working_directory == other.working_directory && @environment_variables == other.environment_variables && @stdin == other.stdin && @stdout == other.stdout && @stderr == other.stderr && @stdout_stderr == other.stdout_stderr && @properties == other.properties
107
+ end
108
+
109
+ alias eql? ==
110
+
111
+ def hash
112
+ [@command_line, @arguments, @response_files, @start_time_utc, @end_time_utc, @exit_code, @rule_configuration_overrides, @notification_configuration_overrides, @tool_execution_notifications, @tool_configuration_notifications, @exit_code_description, @exit_signal_name, @exit_signal_number, @process_start_failure_message, @execution_successful, @machine, @account, @process_id, @executable_location, @working_directory, @environment_variables, @stdin, @stdout, @stderr, @stdout_stderr, @properties].hash
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A location within a programming artifact.
5
+ class Location
6
+ attr_accessor :id, :physical_location, :logical_locations, :message, :annotations, :relationships, :properties
7
+
8
+ def initialize(id: -1, physical_location: nil, logical_locations: [], message: nil, annotations: [], relationships: [], properties: nil)
9
+ @id = id
10
+ @physical_location = physical_location
11
+ @logical_locations = logical_locations
12
+ @message = message
13
+ @annotations = annotations
14
+ @relationships = relationships
15
+ @properties = properties
16
+ end
17
+
18
+ def to_h
19
+ h = {}
20
+ h["id"] = @id if @id && @id != -1
21
+ h["physicalLocation"] = @physical_location&.to_h unless @physical_location.nil?
22
+ h["logicalLocations"] = @logical_locations&.map(&:to_h) if @logical_locations&.any?
23
+ h["message"] = @message&.to_h unless @message.nil?
24
+ h["annotations"] = @annotations&.map(&:to_h) if @annotations&.any?
25
+ h["relationships"] = @relationships&.map(&:to_h) if @relationships&.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
+ id: h["id"] || -1,
38
+ physical_location: PhysicalLocation.from_hash(h["physicalLocation"]),
39
+ logical_locations: h["logicalLocations"]&.map { |v| LogicalLocation.from_hash(v) } || [],
40
+ message: Message.from_hash(h["message"]),
41
+ annotations: h["annotations"]&.map { |v| Region.from_hash(v) } || [],
42
+ relationships: h["relationships"]&.map { |v| LocationRelationship.from_hash(v) } || [],
43
+ properties: h["properties"]
44
+ )
45
+ end
46
+
47
+ def ==(other)
48
+ return false unless other.is_a?(Location)
49
+ @id == other.id && @physical_location == other.physical_location && @logical_locations == other.logical_locations && @message == other.message && @annotations == other.annotations && @relationships == other.relationships && @properties == other.properties
50
+ end
51
+
52
+ alias eql? ==
53
+
54
+ def hash
55
+ [@id, @physical_location, @logical_locations, @message, @annotations, @relationships, @properties].hash
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Information about the relation of one location to another.
5
+ class LocationRelationship
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
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: 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?(LocationRelationship)
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
data/lib/sarif/log.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Static Analysis Results Format (SARIF) Version 2.1.0 JSON Schema: a standard format for the output of static analysis tools.
5
+ class Log
6
+ attr_accessor :schema_uri, :version, :runs, :inline_external_properties, :properties
7
+
8
+ def initialize(schema_uri: nil, version:, runs:, inline_external_properties: nil, properties: nil)
9
+ @schema_uri = schema_uri
10
+ @version = version
11
+ @runs = runs
12
+ @inline_external_properties = inline_external_properties
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["$schema"] = @schema_uri unless @schema_uri.nil?
19
+ h["version"] = @version&.to_s
20
+ h["runs"] = @runs&.map(&:to_h)
21
+ h["inlineExternalProperties"] = @inline_external_properties&.map(&:to_h) if @inline_external_properties&.any?
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
+ schema_uri: h["$schema"],
34
+ version: h["version"],
35
+ runs: h["runs"]&.map { |v| Run.from_hash(v) },
36
+ inline_external_properties: h["inlineExternalProperties"]&.map { |v| ExternalProperties.from_hash(v) },
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(Log)
43
+ @schema_uri == other.schema_uri && @version == other.version && @runs == other.runs && @inline_external_properties == other.inline_external_properties && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@schema_uri, @version, @runs, @inline_external_properties, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A logical location of a construct that produced a result.
5
+ class LogicalLocation
6
+ attr_accessor :name, :index, :fully_qualified_name, :decorated_name, :parent_index, :kind, :properties
7
+
8
+ def initialize(name: nil, index: -1, fully_qualified_name: nil, decorated_name: nil, parent_index: -1, kind: nil, properties: nil)
9
+ @name = name
10
+ @index = index
11
+ @fully_qualified_name = fully_qualified_name
12
+ @decorated_name = decorated_name
13
+ @parent_index = parent_index
14
+ @kind = kind
15
+ @properties = properties
16
+ end
17
+
18
+ def to_h
19
+ h = {}
20
+ h["name"] = @name unless @name.nil?
21
+ h["index"] = @index if @index && @index != -1
22
+ h["fullyQualifiedName"] = @fully_qualified_name unless @fully_qualified_name.nil?
23
+ h["decoratedName"] = @decorated_name unless @decorated_name.nil?
24
+ h["parentIndex"] = @parent_index if @parent_index && @parent_index != -1
25
+ h["kind"] = @kind unless @kind.nil?
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
+ name: h["name"],
38
+ index: h["index"] || -1,
39
+ fully_qualified_name: h["fullyQualifiedName"],
40
+ decorated_name: h["decoratedName"],
41
+ parent_index: h["parentIndex"] || -1,
42
+ kind: h["kind"],
43
+ properties: h["properties"]
44
+ )
45
+ end
46
+
47
+ def ==(other)
48
+ return false unless other.is_a?(LogicalLocation)
49
+ @name == other.name && @index == other.index && @fully_qualified_name == other.fully_qualified_name && @decorated_name == other.decorated_name && @parent_index == other.parent_index && @kind == other.kind && @properties == other.properties
50
+ end
51
+
52
+ alias eql? ==
53
+
54
+ def hash
55
+ [@name, @index, @fully_qualified_name, @decorated_name, @parent_index, @kind, @properties].hash
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Encapsulates a message intended to be read by the end user.
5
+ class Message
6
+ attr_accessor :text, :markdown, :id, :arguments, :properties
7
+
8
+ def initialize(text: nil, markdown: nil, id: nil, arguments: [], properties: nil)
9
+ @text = text
10
+ @markdown = markdown
11
+ @id = id
12
+ @arguments = arguments
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["text"] = @text unless @text.nil?
19
+ h["markdown"] = @markdown unless @markdown.nil?
20
+ h["id"] = @id unless @id.nil?
21
+ h["arguments"] = @arguments if @arguments&.any?
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
+ text: h["text"],
34
+ markdown: h["markdown"],
35
+ id: h["id"],
36
+ arguments: h["arguments"] || [],
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(Message)
43
+ @text == other.text && @markdown == other.markdown && @id == other.id && @arguments == other.arguments && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@text, @markdown, @id, @arguments, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A message string or message format string rendered in multiple formats.
5
+ class MultiformatMessageString
6
+ attr_accessor :text, :markdown, :properties
7
+
8
+ def initialize(text:, markdown: nil, properties: nil)
9
+ @text = text
10
+ @markdown = markdown
11
+ @properties = properties
12
+ end
13
+
14
+ def to_h
15
+ h = {}
16
+ h["text"] = @text
17
+ h["markdown"] = @markdown unless @markdown.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
+ text: h["text"],
30
+ markdown: h["markdown"],
31
+ properties: h["properties"]
32
+ )
33
+ end
34
+
35
+ def ==(other)
36
+ return false unless other.is_a?(MultiformatMessageString)
37
+ @text == other.text && @markdown == other.markdown && @properties == other.properties
38
+ end
39
+
40
+ alias eql? ==
41
+
42
+ def hash
43
+ [@text, @markdown, @properties].hash
44
+ end
45
+ end
46
+ end
data/lib/sarif/node.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Represents a node in a graph.
5
+ class Node
6
+ attr_accessor :id, :label, :location, :children, :properties
7
+
8
+ def initialize(id:, label: nil, location: nil, children: [], properties: nil)
9
+ @id = id
10
+ @label = label
11
+ @location = location
12
+ @children = children
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["id"] = @id
19
+ h["label"] = @label&.to_h unless @label.nil?
20
+ h["location"] = @location&.to_h unless @location.nil?
21
+ h["children"] = @children&.map(&:to_h) if @children&.any?
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
+ label: Message.from_hash(h["label"]),
35
+ location: Location.from_hash(h["location"]),
36
+ children: h["children"]&.map { |v| Node.from_hash(v) } || [],
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(Node)
43
+ @id == other.id && @label == other.label && @location == other.location && @children == other.children && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@id, @label, @location, @children, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Describes a condition relevant to the tool itself, as opposed to being relevant to a target being analyzed by the tool.
5
+ class Notification
6
+ attr_accessor :locations, :message, :level, :thread_id, :time_utc, :exception, :descriptor, :associated_rule, :properties
7
+
8
+ def initialize(locations: [], message:, level: "warning", thread_id: nil, time_utc: nil, exception: nil, descriptor: nil, associated_rule: nil, properties: nil)
9
+ @locations = locations
10
+ @message = message
11
+ @level = level
12
+ @thread_id = thread_id
13
+ @time_utc = time_utc
14
+ @exception = exception
15
+ @descriptor = descriptor
16
+ @associated_rule = associated_rule
17
+ @properties = properties
18
+ end
19
+
20
+ def to_h
21
+ h = {}
22
+ h["locations"] = @locations&.map(&:to_h) if @locations&.any?
23
+ h["message"] = @message&.to_h
24
+ h["level"] = @level&.to_s if @level && @level != "warning"
25
+ h["threadId"] = @thread_id unless @thread_id.nil?
26
+ h["timeUtc"] = @time_utc unless @time_utc.nil?
27
+ h["exception"] = @exception&.to_h unless @exception.nil?
28
+ h["descriptor"] = @descriptor&.to_h unless @descriptor.nil?
29
+ h["associatedRule"] = @associated_rule&.to_h unless @associated_rule.nil?
30
+ h["properties"] = @properties unless @properties.nil?
31
+ h
32
+ end
33
+
34
+ def to_json(pretty: false)
35
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
36
+ end
37
+
38
+ def self.from_hash(h)
39
+ return nil if h.nil?
40
+ new(
41
+ locations: h["locations"]&.map { |v| Location.from_hash(v) } || [],
42
+ message: Message.from_hash(h["message"]),
43
+ level: h["level"] || "warning",
44
+ thread_id: h["threadId"],
45
+ time_utc: h["timeUtc"],
46
+ exception: Exception.from_hash(h["exception"]),
47
+ descriptor: ReportingDescriptorReference.from_hash(h["descriptor"]),
48
+ associated_rule: ReportingDescriptorReference.from_hash(h["associatedRule"]),
49
+ properties: h["properties"]
50
+ )
51
+ end
52
+
53
+ def ==(other)
54
+ return false unless other.is_a?(Notification)
55
+ @locations == other.locations && @message == other.message && @level == other.level && @thread_id == other.thread_id && @time_utc == other.time_utc && @exception == other.exception && @descriptor == other.descriptor && @associated_rule == other.associated_rule && @properties == other.properties
56
+ end
57
+
58
+ alias eql? ==
59
+
60
+ def hash
61
+ [@locations, @message, @level, @thread_id, @time_utc, @exception, @descriptor, @associated_rule, @properties].hash
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # A physical location relevant to a result. Specifies a reference to a programming artifact together with a range of bytes or characters within that artifact.
5
+ class PhysicalLocation
6
+ attr_accessor :address, :artifact_location, :region, :context_region, :properties
7
+
8
+ def initialize(address: nil, artifact_location: nil, region: nil, context_region: nil, properties: nil)
9
+ @address = address
10
+ @artifact_location = artifact_location
11
+ @region = region
12
+ @context_region = context_region
13
+ @properties = properties
14
+ end
15
+
16
+ def to_h
17
+ h = {}
18
+ h["address"] = @address&.to_h unless @address.nil?
19
+ h["artifactLocation"] = @artifact_location&.to_h unless @artifact_location.nil?
20
+ h["region"] = @region&.to_h unless @region.nil?
21
+ h["contextRegion"] = @context_region&.to_h unless @context_region.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
+ address: Address.from_hash(h["address"]),
34
+ artifact_location: ArtifactLocation.from_hash(h["artifactLocation"]),
35
+ region: Region.from_hash(h["region"]),
36
+ context_region: Region.from_hash(h["contextRegion"]),
37
+ properties: h["properties"]
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ return false unless other.is_a?(PhysicalLocation)
43
+ @address == other.address && @artifact_location == other.artifact_location && @region == other.region && @context_region == other.context_region && @properties == other.properties
44
+ end
45
+
46
+ alias eql? ==
47
+
48
+ def hash
49
+ [@address, @artifact_location, @region, @context_region, @properties].hash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sarif
4
+ # Key/value pairs that provide additional information about the object.
5
+ class PropertyBag
6
+ attr_accessor :tags
7
+
8
+ def initialize(tags: [])
9
+ @tags = tags
10
+ end
11
+
12
+ def to_h
13
+ h = {}
14
+ h["tags"] = @tags if @tags&.any?
15
+ h
16
+ end
17
+
18
+ def to_json(pretty: false)
19
+ pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
20
+ end
21
+
22
+ def self.from_hash(h)
23
+ return nil if h.nil?
24
+ new(
25
+ tags: h["tags"] || []
26
+ )
27
+ end
28
+
29
+ def ==(other)
30
+ return false unless other.is_a?(PropertyBag)
31
+ @tags == other.tags
32
+ end
33
+
34
+ alias eql? ==
35
+
36
+ def hash
37
+ [@tags].hash
38
+ end
39
+ end
40
+ end