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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE +21 -0
- data/README.md +191 -0
- data/Rakefile +10 -0
- data/lib/sarif/address.rb +67 -0
- data/lib/sarif/artifact.rb +76 -0
- data/lib/sarif/artifact_change.rb +46 -0
- data/lib/sarif/artifact_content.rb +49 -0
- data/lib/sarif/artifact_location.rb +52 -0
- data/lib/sarif/attachment.rb +52 -0
- data/lib/sarif/code_flow.rb +46 -0
- data/lib/sarif/configuration_override.rb +46 -0
- data/lib/sarif/conversion.rb +49 -0
- data/lib/sarif/edge.rb +52 -0
- data/lib/sarif/edge_traversal.rb +52 -0
- data/lib/sarif/exception.rb +52 -0
- data/lib/sarif/external_properties.rb +100 -0
- data/lib/sarif/external_property_file_reference.rb +49 -0
- data/lib/sarif/external_property_file_references.rb +88 -0
- data/lib/sarif/fix.rb +46 -0
- data/lib/sarif/graph.rb +49 -0
- data/lib/sarif/graph_traversal.rb +58 -0
- data/lib/sarif/invocation.rb +115 -0
- data/lib/sarif/location.rb +58 -0
- data/lib/sarif/location_relationship.rb +49 -0
- data/lib/sarif/log.rb +52 -0
- data/lib/sarif/logical_location.rb +58 -0
- data/lib/sarif/message.rb +52 -0
- data/lib/sarif/multiformat_message_string.rb +46 -0
- data/lib/sarif/node.rb +52 -0
- data/lib/sarif/notification.rb +64 -0
- data/lib/sarif/physical_location.rb +52 -0
- data/lib/sarif/property_bag.rb +40 -0
- data/lib/sarif/rectangle.rb +55 -0
- data/lib/sarif/region.rb +73 -0
- data/lib/sarif/replacement.rb +46 -0
- data/lib/sarif/reporting_configuration.rb +52 -0
- data/lib/sarif/reporting_descriptor.rb +79 -0
- data/lib/sarif/reporting_descriptor_reference.rb +52 -0
- data/lib/sarif/reporting_descriptor_relationship.rb +49 -0
- data/lib/sarif/result.rb +127 -0
- data/lib/sarif/result_provenance.rb +58 -0
- data/lib/sarif/run.rb +121 -0
- data/lib/sarif/run_automation_details.rb +52 -0
- data/lib/sarif/schema/sarif-schema-2.1.0.json +3389 -0
- data/lib/sarif/special_locations.rb +43 -0
- data/lib/sarif/stack.rb +46 -0
- data/lib/sarif/stack_frame.rb +52 -0
- data/lib/sarif/suppression.rb +55 -0
- data/lib/sarif/thread_flow.rb +55 -0
- data/lib/sarif/thread_flow_location.rb +79 -0
- data/lib/sarif/tool.rb +46 -0
- data/lib/sarif/tool_component.rb +121 -0
- data/lib/sarif/tool_component_reference.rb +49 -0
- data/lib/sarif/translation_metadata.rb +58 -0
- data/lib/sarif/version.rb +5 -0
- data/lib/sarif/version_control_details.rb +58 -0
- data/lib/sarif/web_request.rb +64 -0
- data/lib/sarif/web_response.rb +64 -0
- data/lib/sarif.rb +121 -0
- data/sig/sarif.rbs +4 -0
- metadata +106 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 79bce3486dd120e26e0263dcb1bb6b2a9d927c5e75390563eaa78c08cab73065
|
|
4
|
+
data.tar.gz: a8265311c18e62d73ed428b75f7a019355a2122566569e67f8095aa3b3322df0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: acaaf4c0aee7cbbb54efa9eb7a77d8d2ecf5a033fde1fa7a7606dc1b6559b38d4bf745f38349a5c7e9c0f36ebcbf84bc9dfd84a0ef681f155775be426b8a11f2
|
|
7
|
+
data.tar.gz: 12337301a09f56547993e8468d632b6b1d9816e58e9cf0378b5bf5eb2045477dc2e7e24672ff2fa681561d5980b096b6ca141d5136827cbd26ce3e45707774f8
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"sarif" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["andrewnez@gmail.com"](mailto:"andrewnez@gmail.com").
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Andrew Nesbitt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# sarif-ruby
|
|
2
|
+
|
|
3
|
+
A Ruby SDK for SARIF (Static Analysis Results Interchange Format) 2.1.0.
|
|
4
|
+
|
|
5
|
+
SARIF is an OASIS standard format for representing static analysis tool output. This gem provides Ruby classes for creating, reading, and manipulating SARIF files.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem "sarif-ruby"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install directly:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
gem install sarif-ruby
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Creating SARIF output
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
require "sarif"
|
|
27
|
+
|
|
28
|
+
log = Sarif::Log.new(
|
|
29
|
+
version: "2.1.0",
|
|
30
|
+
runs: [
|
|
31
|
+
Sarif::Run.new(
|
|
32
|
+
tool: Sarif::Tool.new(
|
|
33
|
+
driver: Sarif::ToolComponent.new(
|
|
34
|
+
name: "my-linter",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
information_uri: "https://example.com/my-linter"
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
results: [
|
|
40
|
+
Sarif::Result.new(
|
|
41
|
+
rule_id: "no-unused-vars",
|
|
42
|
+
level: "warning",
|
|
43
|
+
message: Sarif::Message.new(text: "Variable 'x' is unused"),
|
|
44
|
+
locations: [
|
|
45
|
+
Sarif::Location.new(
|
|
46
|
+
physical_location: Sarif::PhysicalLocation.new(
|
|
47
|
+
artifact_location: Sarif::ArtifactLocation.new(uri: "src/main.rb"),
|
|
48
|
+
region: Sarif::Region.new(start_line: 10, start_column: 5)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
]
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Write to file
|
|
59
|
+
Sarif.dump(log, "results.sarif")
|
|
60
|
+
|
|
61
|
+
# Write pretty-printed JSON
|
|
62
|
+
Sarif.dump(log, "results.sarif", pretty: true)
|
|
63
|
+
|
|
64
|
+
# Get JSON string
|
|
65
|
+
json = log.to_json(pretty: true)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Reading SARIF files
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# Load from file
|
|
72
|
+
log = Sarif.load("results.sarif")
|
|
73
|
+
|
|
74
|
+
# Parse JSON string
|
|
75
|
+
log = Sarif.parse(json_string)
|
|
76
|
+
|
|
77
|
+
# Access data
|
|
78
|
+
log.runs.each do |run|
|
|
79
|
+
puts "Tool: #{run.tool.driver.name}"
|
|
80
|
+
|
|
81
|
+
run.results&.each do |result|
|
|
82
|
+
puts " #{result.rule_id}: #{result.message.text}"
|
|
83
|
+
|
|
84
|
+
result.locations&.each do |location|
|
|
85
|
+
loc = location.physical_location
|
|
86
|
+
puts " #{loc.artifact_location.uri}:#{loc.region&.start_line}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Defining rules
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
Sarif::Run.new(
|
|
96
|
+
tool: Sarif::Tool.new(
|
|
97
|
+
driver: Sarif::ToolComponent.new(
|
|
98
|
+
name: "my-linter",
|
|
99
|
+
version: "1.0.0",
|
|
100
|
+
rules: [
|
|
101
|
+
Sarif::ReportingDescriptor.new(
|
|
102
|
+
id: "no-unused-vars",
|
|
103
|
+
name: "NoUnusedVariables",
|
|
104
|
+
short_description: Sarif::MultiformatMessageString.new(
|
|
105
|
+
text: "Disallow unused variables"
|
|
106
|
+
),
|
|
107
|
+
full_description: Sarif::MultiformatMessageString.new(
|
|
108
|
+
text: "Variables that are declared but never used are likely mistakes."
|
|
109
|
+
),
|
|
110
|
+
default_configuration: Sarif::ReportingConfiguration.new(
|
|
111
|
+
level: "warning"
|
|
112
|
+
),
|
|
113
|
+
help_uri: "https://example.com/rules/no-unused-vars"
|
|
114
|
+
)
|
|
115
|
+
]
|
|
116
|
+
)
|
|
117
|
+
),
|
|
118
|
+
results: [
|
|
119
|
+
Sarif::Result.new(
|
|
120
|
+
rule_id: "no-unused-vars",
|
|
121
|
+
rule_index: 0,
|
|
122
|
+
message: Sarif::Message.new(text: "Variable 'x' is unused")
|
|
123
|
+
)
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Result levels
|
|
129
|
+
|
|
130
|
+
SARIF defines four severity levels:
|
|
131
|
+
|
|
132
|
+
- `"error"` - A serious problem
|
|
133
|
+
- `"warning"` - A potential problem (default)
|
|
134
|
+
- `"note"` - Informational finding
|
|
135
|
+
- `"none"` - No severity
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
Sarif::Result.new(
|
|
139
|
+
rule_id: "security-issue",
|
|
140
|
+
level: "error",
|
|
141
|
+
message: Sarif::Message.new(text: "SQL injection vulnerability")
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Available classes
|
|
146
|
+
|
|
147
|
+
The gem provides classes for all SARIF 2.1.0 types:
|
|
148
|
+
|
|
149
|
+
| Class | Description |
|
|
150
|
+
|-------|-------------|
|
|
151
|
+
| `Sarif::Log` | Root object containing runs |
|
|
152
|
+
| `Sarif::Run` | Single tool execution |
|
|
153
|
+
| `Sarif::Tool` | Tool metadata |
|
|
154
|
+
| `Sarif::ToolComponent` | Tool driver or extension |
|
|
155
|
+
| `Sarif::Result` | Individual finding |
|
|
156
|
+
| `Sarif::Message` | Human-readable message |
|
|
157
|
+
| `Sarif::Location` | Where a result was detected |
|
|
158
|
+
| `Sarif::PhysicalLocation` | File and region |
|
|
159
|
+
| `Sarif::ArtifactLocation` | File path or URI |
|
|
160
|
+
| `Sarif::Region` | Line/column range |
|
|
161
|
+
| `Sarif::ReportingDescriptor` | Rule definition |
|
|
162
|
+
| `Sarif::ReportingConfiguration` | Rule configuration |
|
|
163
|
+
| `Sarif::Fix` | Proposed fix |
|
|
164
|
+
| `Sarif::Invocation` | Tool execution details |
|
|
165
|
+
| ... | And 40+ more |
|
|
166
|
+
|
|
167
|
+
## Regenerating classes
|
|
168
|
+
|
|
169
|
+
Classes are generated from the official SARIF JSON schema. To regenerate:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
bundle exec rake sarif:generate
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Links
|
|
176
|
+
|
|
177
|
+
- [SARIF Specification](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html)
|
|
178
|
+
- [SARIF JSON Schema](https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json)
|
|
179
|
+
- [SARIF Tutorials](https://github.com/microsoft/sarif-tutorials)
|
|
180
|
+
- [OASIS SARIF TC](https://www.oasis-open.org/committees/sarif/)
|
|
181
|
+
|
|
182
|
+
### Other SARIF SDKs
|
|
183
|
+
|
|
184
|
+
- [sarif-python-om](https://github.com/microsoft/sarif-python-om) - Python
|
|
185
|
+
- [java-sarif](https://github.com/Contrast-Security-OSS/java-sarif) - Java
|
|
186
|
+
- [sarif-sdk](https://github.com/microsoft/sarif-sdk) - .NET
|
|
187
|
+
- [sarif-js-sdk](https://github.com/microsoft/sarif-js-sdk) - JavaScript
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# A physical or virtual address, or a range of addresses, in an 'addressable region' (memory or a binary file).
|
|
5
|
+
class Address
|
|
6
|
+
attr_accessor :absolute_address, :relative_address, :length, :kind, :name, :fully_qualified_name, :offset_from_parent, :index, :parent_index, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(absolute_address: -1, relative_address: nil, length: nil, kind: nil, name: nil, fully_qualified_name: nil, offset_from_parent: nil, index: -1, parent_index: -1, properties: nil)
|
|
9
|
+
@absolute_address = absolute_address
|
|
10
|
+
@relative_address = relative_address
|
|
11
|
+
@length = length
|
|
12
|
+
@kind = kind
|
|
13
|
+
@name = name
|
|
14
|
+
@fully_qualified_name = fully_qualified_name
|
|
15
|
+
@offset_from_parent = offset_from_parent
|
|
16
|
+
@index = index
|
|
17
|
+
@parent_index = parent_index
|
|
18
|
+
@properties = properties
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_h
|
|
22
|
+
h = {}
|
|
23
|
+
h["absoluteAddress"] = @absolute_address if @absolute_address && @absolute_address != -1
|
|
24
|
+
h["relativeAddress"] = @relative_address unless @relative_address.nil?
|
|
25
|
+
h["length"] = @length unless @length.nil?
|
|
26
|
+
h["kind"] = @kind unless @kind.nil?
|
|
27
|
+
h["name"] = @name unless @name.nil?
|
|
28
|
+
h["fullyQualifiedName"] = @fully_qualified_name unless @fully_qualified_name.nil?
|
|
29
|
+
h["offsetFromParent"] = @offset_from_parent unless @offset_from_parent.nil?
|
|
30
|
+
h["index"] = @index if @index && @index != -1
|
|
31
|
+
h["parentIndex"] = @parent_index if @parent_index && @parent_index != -1
|
|
32
|
+
h["properties"] = @properties unless @properties.nil?
|
|
33
|
+
h
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_json(pretty: false)
|
|
37
|
+
pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.from_hash(h)
|
|
41
|
+
return nil if h.nil?
|
|
42
|
+
new(
|
|
43
|
+
absolute_address: h["absoluteAddress"] || -1,
|
|
44
|
+
relative_address: h["relativeAddress"],
|
|
45
|
+
length: h["length"],
|
|
46
|
+
kind: h["kind"],
|
|
47
|
+
name: h["name"],
|
|
48
|
+
fully_qualified_name: h["fullyQualifiedName"],
|
|
49
|
+
offset_from_parent: h["offsetFromParent"],
|
|
50
|
+
index: h["index"] || -1,
|
|
51
|
+
parent_index: h["parentIndex"] || -1,
|
|
52
|
+
properties: h["properties"]
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ==(other)
|
|
57
|
+
return false unless other.is_a?(Address)
|
|
58
|
+
@absolute_address == other.absolute_address && @relative_address == other.relative_address && @length == other.length && @kind == other.kind && @name == other.name && @fully_qualified_name == other.fully_qualified_name && @offset_from_parent == other.offset_from_parent && @index == other.index && @parent_index == other.parent_index && @properties == other.properties
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
alias eql? ==
|
|
62
|
+
|
|
63
|
+
def hash
|
|
64
|
+
[@absolute_address, @relative_address, @length, @kind, @name, @fully_qualified_name, @offset_from_parent, @index, @parent_index, @properties].hash
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# A single artifact. In some cases, this artifact might be nested within another artifact.
|
|
5
|
+
class Artifact
|
|
6
|
+
attr_accessor :description, :location, :parent_index, :offset, :length, :roles, :mime_type, :contents, :encoding, :source_language, :hashes, :last_modified_time_utc, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(description: nil, location: nil, parent_index: -1, offset: nil, length: -1, roles: [], mime_type: nil, contents: nil, encoding: nil, source_language: nil, hashes: nil, last_modified_time_utc: nil, properties: nil)
|
|
9
|
+
@description = description
|
|
10
|
+
@location = location
|
|
11
|
+
@parent_index = parent_index
|
|
12
|
+
@offset = offset
|
|
13
|
+
@length = length
|
|
14
|
+
@roles = roles
|
|
15
|
+
@mime_type = mime_type
|
|
16
|
+
@contents = contents
|
|
17
|
+
@encoding = encoding
|
|
18
|
+
@source_language = source_language
|
|
19
|
+
@hashes = hashes
|
|
20
|
+
@last_modified_time_utc = last_modified_time_utc
|
|
21
|
+
@properties = properties
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
h = {}
|
|
26
|
+
h["description"] = @description&.to_h unless @description.nil?
|
|
27
|
+
h["location"] = @location&.to_h unless @location.nil?
|
|
28
|
+
h["parentIndex"] = @parent_index if @parent_index && @parent_index != -1
|
|
29
|
+
h["offset"] = @offset unless @offset.nil?
|
|
30
|
+
h["length"] = @length if @length && @length != -1
|
|
31
|
+
h["roles"] = @roles&.map(&:to_s) if @roles&.any?
|
|
32
|
+
h["mimeType"] = @mime_type unless @mime_type.nil?
|
|
33
|
+
h["contents"] = @contents&.to_h unless @contents.nil?
|
|
34
|
+
h["encoding"] = @encoding unless @encoding.nil?
|
|
35
|
+
h["sourceLanguage"] = @source_language unless @source_language.nil?
|
|
36
|
+
h["hashes"] = @hashes unless @hashes.nil?
|
|
37
|
+
h["lastModifiedTimeUtc"] = @last_modified_time_utc unless @last_modified_time_utc.nil?
|
|
38
|
+
h["properties"] = @properties unless @properties.nil?
|
|
39
|
+
h
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_json(pretty: false)
|
|
43
|
+
pretty ? JSON.pretty_generate(to_h) : JSON.generate(to_h)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.from_hash(h)
|
|
47
|
+
return nil if h.nil?
|
|
48
|
+
new(
|
|
49
|
+
description: Message.from_hash(h["description"]),
|
|
50
|
+
location: ArtifactLocation.from_hash(h["location"]),
|
|
51
|
+
parent_index: h["parentIndex"] || -1,
|
|
52
|
+
offset: h["offset"],
|
|
53
|
+
length: h["length"] || -1,
|
|
54
|
+
roles: h["roles"] || [],
|
|
55
|
+
mime_type: h["mimeType"],
|
|
56
|
+
contents: ArtifactContent.from_hash(h["contents"]),
|
|
57
|
+
encoding: h["encoding"],
|
|
58
|
+
source_language: h["sourceLanguage"],
|
|
59
|
+
hashes: h["hashes"],
|
|
60
|
+
last_modified_time_utc: h["lastModifiedTimeUtc"],
|
|
61
|
+
properties: h["properties"]
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def ==(other)
|
|
66
|
+
return false unless other.is_a?(Artifact)
|
|
67
|
+
@description == other.description && @location == other.location && @parent_index == other.parent_index && @offset == other.offset && @length == other.length && @roles == other.roles && @mime_type == other.mime_type && @contents == other.contents && @encoding == other.encoding && @source_language == other.source_language && @hashes == other.hashes && @last_modified_time_utc == other.last_modified_time_utc && @properties == other.properties
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
alias eql? ==
|
|
71
|
+
|
|
72
|
+
def hash
|
|
73
|
+
[@description, @location, @parent_index, @offset, @length, @roles, @mime_type, @contents, @encoding, @source_language, @hashes, @last_modified_time_utc, @properties].hash
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# A change to a single artifact.
|
|
5
|
+
class ArtifactChange
|
|
6
|
+
attr_accessor :artifact_location, :replacements, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(artifact_location:, replacements:, properties: nil)
|
|
9
|
+
@artifact_location = artifact_location
|
|
10
|
+
@replacements = replacements
|
|
11
|
+
@properties = properties
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
h = {}
|
|
16
|
+
h["artifactLocation"] = @artifact_location&.to_h
|
|
17
|
+
h["replacements"] = @replacements&.map(&:to_h)
|
|
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
|
+
artifact_location: ArtifactLocation.from_hash(h["artifactLocation"]),
|
|
30
|
+
replacements: h["replacements"]&.map { |v| Replacement.from_hash(v) },
|
|
31
|
+
properties: h["properties"]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(other)
|
|
36
|
+
return false unless other.is_a?(ArtifactChange)
|
|
37
|
+
@artifact_location == other.artifact_location && @replacements == other.replacements && @properties == other.properties
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias eql? ==
|
|
41
|
+
|
|
42
|
+
def hash
|
|
43
|
+
[@artifact_location, @replacements, @properties].hash
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# Represents the contents of an artifact.
|
|
5
|
+
class ArtifactContent
|
|
6
|
+
attr_accessor :text, :binary, :rendered, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(text: nil, binary: nil, rendered: nil, properties: nil)
|
|
9
|
+
@text = text
|
|
10
|
+
@binary = binary
|
|
11
|
+
@rendered = rendered
|
|
12
|
+
@properties = properties
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_h
|
|
16
|
+
h = {}
|
|
17
|
+
h["text"] = @text unless @text.nil?
|
|
18
|
+
h["binary"] = @binary unless @binary.nil?
|
|
19
|
+
h["rendered"] = @rendered&.to_h unless @rendered.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
|
+
text: h["text"],
|
|
32
|
+
binary: h["binary"],
|
|
33
|
+
rendered: MultiformatMessageString.from_hash(h["rendered"]),
|
|
34
|
+
properties: h["properties"]
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def ==(other)
|
|
39
|
+
return false unless other.is_a?(ArtifactContent)
|
|
40
|
+
@text == other.text && @binary == other.binary && @rendered == other.rendered && @properties == other.properties
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
alias eql? ==
|
|
44
|
+
|
|
45
|
+
def hash
|
|
46
|
+
[@text, @binary, @rendered, @properties].hash
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# Specifies the location of an artifact.
|
|
5
|
+
class ArtifactLocation
|
|
6
|
+
attr_accessor :uri, :uri_base_id, :index, :description, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(uri: nil, uri_base_id: nil, index: -1, description: nil, properties: nil)
|
|
9
|
+
@uri = uri
|
|
10
|
+
@uri_base_id = uri_base_id
|
|
11
|
+
@index = index
|
|
12
|
+
@description = description
|
|
13
|
+
@properties = properties
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
h = {}
|
|
18
|
+
h["uri"] = @uri unless @uri.nil?
|
|
19
|
+
h["uriBaseId"] = @uri_base_id unless @uri_base_id.nil?
|
|
20
|
+
h["index"] = @index if @index && @index != -1
|
|
21
|
+
h["description"] = @description&.to_h unless @description.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
|
+
uri: h["uri"],
|
|
34
|
+
uri_base_id: h["uriBaseId"],
|
|
35
|
+
index: h["index"] || -1,
|
|
36
|
+
description: Message.from_hash(h["description"]),
|
|
37
|
+
properties: h["properties"]
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def ==(other)
|
|
42
|
+
return false unless other.is_a?(ArtifactLocation)
|
|
43
|
+
@uri == other.uri && @uri_base_id == other.uri_base_id && @index == other.index && @description == other.description && @properties == other.properties
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
alias eql? ==
|
|
47
|
+
|
|
48
|
+
def hash
|
|
49
|
+
[@uri, @uri_base_id, @index, @description, @properties].hash
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# An artifact relevant to a result.
|
|
5
|
+
class Attachment
|
|
6
|
+
attr_accessor :description, :artifact_location, :regions, :rectangles, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(description: nil, artifact_location:, regions: [], rectangles: [], properties: nil)
|
|
9
|
+
@description = description
|
|
10
|
+
@artifact_location = artifact_location
|
|
11
|
+
@regions = regions
|
|
12
|
+
@rectangles = rectangles
|
|
13
|
+
@properties = properties
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
h = {}
|
|
18
|
+
h["description"] = @description&.to_h unless @description.nil?
|
|
19
|
+
h["artifactLocation"] = @artifact_location&.to_h
|
|
20
|
+
h["regions"] = @regions&.map(&:to_h) if @regions&.any?
|
|
21
|
+
h["rectangles"] = @rectangles&.map(&:to_h) if @rectangles&.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
|
+
description: Message.from_hash(h["description"]),
|
|
34
|
+
artifact_location: ArtifactLocation.from_hash(h["artifactLocation"]),
|
|
35
|
+
regions: h["regions"]&.map { |v| Region.from_hash(v) } || [],
|
|
36
|
+
rectangles: h["rectangles"]&.map { |v| Rectangle.from_hash(v) } || [],
|
|
37
|
+
properties: h["properties"]
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def ==(other)
|
|
42
|
+
return false unless other.is_a?(Attachment)
|
|
43
|
+
@description == other.description && @artifact_location == other.artifact_location && @regions == other.regions && @rectangles == other.rectangles && @properties == other.properties
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
alias eql? ==
|
|
47
|
+
|
|
48
|
+
def hash
|
|
49
|
+
[@description, @artifact_location, @regions, @rectangles, @properties].hash
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# A set of threadFlows which together describe a pattern of code execution relevant to detecting a result.
|
|
5
|
+
class CodeFlow
|
|
6
|
+
attr_accessor :message, :thread_flows, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(message: nil, thread_flows:, properties: nil)
|
|
9
|
+
@message = message
|
|
10
|
+
@thread_flows = thread_flows
|
|
11
|
+
@properties = properties
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
h = {}
|
|
16
|
+
h["message"] = @message&.to_h unless @message.nil?
|
|
17
|
+
h["threadFlows"] = @thread_flows&.map(&:to_h)
|
|
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
|
+
message: Message.from_hash(h["message"]),
|
|
30
|
+
thread_flows: h["threadFlows"]&.map { |v| ThreadFlow.from_hash(v) },
|
|
31
|
+
properties: h["properties"]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(other)
|
|
36
|
+
return false unless other.is_a?(CodeFlow)
|
|
37
|
+
@message == other.message && @thread_flows == other.thread_flows && @properties == other.properties
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias eql? ==
|
|
41
|
+
|
|
42
|
+
def hash
|
|
43
|
+
[@message, @thread_flows, @properties].hash
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sarif
|
|
4
|
+
# Information about how a specific rule or notification was reconfigured at runtime.
|
|
5
|
+
class ConfigurationOverride
|
|
6
|
+
attr_accessor :configuration, :descriptor, :properties
|
|
7
|
+
|
|
8
|
+
def initialize(configuration:, descriptor:, properties: nil)
|
|
9
|
+
@configuration = configuration
|
|
10
|
+
@descriptor = descriptor
|
|
11
|
+
@properties = properties
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
h = {}
|
|
16
|
+
h["configuration"] = @configuration&.to_h
|
|
17
|
+
h["descriptor"] = @descriptor&.to_h
|
|
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
|
+
configuration: ReportingConfiguration.from_hash(h["configuration"]),
|
|
30
|
+
descriptor: ReportingDescriptorReference.from_hash(h["descriptor"]),
|
|
31
|
+
properties: h["properties"]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(other)
|
|
36
|
+
return false unless other.is_a?(ConfigurationOverride)
|
|
37
|
+
@configuration == other.configuration && @descriptor == other.descriptor && @properties == other.properties
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias eql? ==
|
|
41
|
+
|
|
42
|
+
def hash
|
|
43
|
+
[@configuration, @descriptor, @properties].hash
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|