|
| 1 | +# Predicate type: Tool Assessment |
| 2 | + |
| 3 | +Type URI: https://in-toto.io/attestation/tool-assessment/v0.1 |
| 4 | + |
| 5 | +Version: v0.1.0 |
| 6 | + |
| 7 | +## Purpose |
| 8 | + |
| 9 | +**The tool assessment attestation references the results and metadata associated with tools used to assess software before or after its creation.** It's primary purpose is to provide an immutable attestation of tool assessment of a software so that it can be bundled with its provenance. This enables to mapping of build provenances to assessments on a per build basis and can reflect entire DevSecOps pipeline processes. |
| 10 | + |
| 11 | + However, this attestation can be used to describe the assessment of any target using any tool for any purpose and is not restricted to pipelines. |
| 12 | + |
| 13 | +There are many existing predicates that describe the use of specific tool types. While these predicates are well defined, they are narrowly scoped. There should exist a predicate that is general enough to effectively attest the use of any tool that can be used if a tool type does not have a predicate type yet. The tool assessment attestation type aims to solve this. |
| 14 | + |
| 15 | +- The [cyclonedx](cyclonedx.md) and [spdx](spdx.md) predicate types describe SBOM standards. |
| 16 | +- The [test result](test-result.md) predicate type describes test running tests in the software supply chain. |
| 17 | +- The [vulnerabilities](vulns_02.md) predicate type describes the results of a vulnerability scan. This predicate closely resembles the type of information desired to be captured by the tool assessment attestation but is too narrowly scoped to producers of vulnerability information. |
| 18 | +- The [SCAI](scai.md) predicate type captures functional attribute and integrity information about software and its supply chain. It is the closest predicate for this use-case but fails to cleanly map a result to its tooling while providing appropriate metadata on the tooling or the policy requiring its execution. The tool assessment attestation would serve well as an attribute predicate in the SCAI framework. |
| 19 | + |
| 20 | +Prior existing predicates still have their own important use-cases. This predicate type does not aim to replace them but to provide a specification flexible enough to use for any type of tool. |
| 21 | + |
| 22 | +## Use Cases |
| 23 | + |
| 24 | +### Control Gates |
| 25 | + |
| 26 | +Control gates are an essential and increasingly prevalent requirement in many |
| 27 | +DevSecOps pipelines where continuous integration is adopted as a standard of |
| 28 | +assessment for a piece of software to be deployed and operated. |
| 29 | +Tool assessment attestations enable security control assessors to audit compliance to policy in an immutable format. |
| 30 | + |
| 31 | +### Policy as Code Enabling via Attachment to Build Artifacts |
| 32 | + |
| 33 | +Tool assessment attestations bundled with container build provenance can enable policy-as-code enforcement of containers or software on IT systems. |
| 34 | + |
| 35 | +## Prerequisites |
| 36 | + |
| 37 | +- [in-toto Attestation Framework](https://github.com/in-toto/attestation/blob/main/spec/README.md) |
| 38 | +- Appropriate knowledge in capturing tool metadata and processing results |
| 39 | + |
| 40 | +## Model |
| 41 | + |
| 42 | +This predicate type is based on three parts: describing the tool, its configuration, and the results of the tool. Due to the generalness of the predicate, some fields will be optional. The `summary` field is included as a non-prescribed object for producers to include more specific data to be attested to from the ran tool. |
| 43 | + |
| 44 | +This also defines the `Profile` object type. |
| 45 | + |
| 46 | +## Schema |
| 47 | + |
| 48 | +```jsonc |
| 49 | +{ |
| 50 | + // Standard attestation fields: |
| 51 | + "_type": "https://in-toto.io/Statement/v1", |
| 52 | + "subject": [{ ... }], |
| 53 | + |
| 54 | + // Predicate: |
| 55 | + "predicateType": "https://in-toto.io/attestation/tool-assessment/v1", |
| 56 | + "predicate": { |
| 57 | + "tool": { |
| 58 | + "name": "<NAME>", |
| 59 | + "type": "<TOOL_DESCRIPTION>", |
| 60 | + "uri": "<URI>", |
| 61 | + "version": "<VERSION>" |
| 62 | + }, |
| 63 | + "config": { |
| 64 | + "profiles": [ Profile ], |
| 65 | + "exclusions": ["<EXCLUSIONS>"], |
| 66 | + "files": [ ResourceDescriptor ], // .ignores, config files, etc |
| 67 | + "full_command": "<FULL_CMD_LINE>" |
| 68 | + }, |
| 69 | + "result": "<RESULT>", |
| 70 | + "output": [ ResourceDescriptor ], |
| 71 | + "summary": { /* object */ }, |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +// Profile Object Type |
| 76 | +Profile: { |
| 77 | + "profile": "<PROFILE_NAME_OR_DECRIPTION>", |
| 78 | + "uri": "<PROFILE_URI>", |
| 79 | + "version": "<VERISON>", |
| 80 | + "last_updated": "<TIME_STAMP>", |
| 81 | + "annotations": { /* object */ } |
| 82 | +} |
| 83 | + |
| 84 | +``` |
| 85 | + |
| 86 | +### Parsing Rules |
| 87 | + |
| 88 | +- Consumers MUST ignore unrecognized fields unless otherwise noted. |
| 89 | +- Acceptable formats of the `summary` and `annotations` fields are up to the producer and consumer. |
| 90 | + |
| 91 | +### Fields |
| 92 | + |
| 93 | +**tool, required** object |
| 94 | + |
| 95 | +> Object associated with identifying the tool. |
| 96 | +
|
| 97 | +**tool.name, required** string |
| 98 | + |
| 99 | +>> Name of tool. |
| 100 | +
|
| 101 | +**tool.type, required** string |
| 102 | + |
| 103 | +>> Description of the type of tool (SAST, DAST, SECRETS, etc). |
| 104 | +
|
| 105 | +**tool.uri, required** string (ResourceURI) |
| 106 | + |
| 107 | +>> URI indicating the identity of the source of the tool. |
| 108 | +
|
| 109 | +**tool.version, optional** string |
| 110 | + |
| 111 | +>> Version of the tool. |
| 112 | +
|
| 113 | +**config, required** object |
| 114 | + |
| 115 | +> Object that describes the configuration of the tool. |
| 116 | +> This object should be descriptive enough to reproduce the results of the tool based on its entries. |
| 117 | +
|
| 118 | +**config.profiles, optional** Profile list |
| 119 | + |
| 120 | +>> Contains a list of profiles used in the tool. Profiles describe the set of data that the tool references in its execution that may modify the behavior of the tool or its results. This includes: rulesets, databases, policies, etc. |
| 121 | +
|
| 122 | +**config.exclusions, optional** string list |
| 123 | + |
| 124 | +>> List of deviations from the profile, such as rule IDs, file names, ignores, etc. |
| 125 | +
|
| 126 | +**config.files, optional** ResourceDescriptor list |
| 127 | + |
| 128 | +>> Reference to files used by the tool to modify configuration. |
| 129 | +
|
| 130 | +**config.full_command, required** string |
| 131 | + |
| 132 | +>> Command used to run the tool. |
| 133 | +
|
| 134 | +**result, required** string |
| 135 | + |
| 136 | +> Result of the tool execution. Usually `PASS` or `FAIL`. |
| 137 | +
|
| 138 | +**output, required** ResourceDescriptor list |
| 139 | + |
| 140 | +> Artifacts associated with the result of the execution of the tool. |
| 141 | +
|
| 142 | +**summary, optional** object |
| 143 | + |
| 144 | +> Object containing extra fields associated with the execution of the tool that contribute to the understanding of a tools results. Acceptable formats are up to the producer and consumer of the attestation. |
| 145 | +
|
| 146 | +--- |
| 147 | + |
| 148 | +**Profile.profile, required** string |
| 149 | + |
| 150 | +>> Name or description of the profile. |
| 151 | +
|
| 152 | +**Profile.uri, required** string |
| 153 | + |
| 154 | +>> URI identifying the source of the profile |
| 155 | +
|
| 156 | +**Profile.version, optional** string |
| 157 | + |
| 158 | +>> Version of the profile |
| 159 | +
|
| 160 | +**Profile.last_updated, optional** string |
| 161 | + |
| 162 | +>> Timestamp of the last update of the profile |
| 163 | +
|
| 164 | +**Profile.annoations, optional** object |
| 165 | + |
| 166 | +>> Extraneous data associated with the tool assessment. |
| 167 | +
|
| 168 | +## Example |
| 169 | + |
| 170 | +### Semgrep |
| 171 | + |
| 172 | +```jsonc |
| 173 | +{ |
| 174 | + "_type": "https://in-toto.io/Statement/v1", |
| 175 | + "subject": [{ |
| 176 | + "name": "foo", |
| 177 | + "digest": { "sha256": "78ec328..." } |
| 178 | + }], |
| 179 | + "predicateType": "https://in-toto.io/attestation/tool-assessment/v0.1", |
| 180 | + "predicate": { |
| 181 | + "tool": { |
| 182 | + "name": "Semgrep", |
| 183 | + "type": "SAST", |
| 184 | + "uri": "pkg:github/semgrep/semgrep@984f760", |
| 185 | + "version": "1.139.0" |
| 186 | + }, |
| 187 | + "config": { |
| 188 | + "profiles": [ |
| 189 | + { |
| 190 | + "profile": "Default Python", |
| 191 | + "uri": "https://semgrep.dev/p/python" |
| 192 | + }, |
| 193 | + { |
| 194 | + "profile": "Community Python", |
| 195 | + "uri": "https://github.com/semgrep/semgrep-rules/tree/d375208f04370b4e8d3ca7fe668db6f0465bb643/python", |
| 196 | + "last_updated": "2025-06-04T19:25:00Z" |
| 197 | + }], |
| 198 | + "exclusions": ["bar.py"], |
| 199 | + "full_command": "semgrep scan --config p/python --config rules/python --exclude='bar.py'" |
| 200 | + }, |
| 201 | + "result": "PASS", |
| 202 | + "output": ["<ResourceDescriptor(semgrep_output.txt)>"] |
| 203 | + } |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +### Trufflehog |
| 208 | + |
| 209 | +```jsonc |
| 210 | +{ |
| 211 | + "_type": "https://in-toto.io/Statement/v1", |
| 212 | + "subject": [{ |
| 213 | + "name": "foo", |
| 214 | + "digest": { "sha256": "78ec328..." } |
| 215 | + }], |
| 216 | + "predicateType": "https://in-toto.io/attestation/tool-assessment/v0.1", |
| 217 | + "predicate": { |
| 218 | + "tool": { |
| 219 | + "name": "Trufflehog", |
| 220 | + "type": "Secrets Scanning", |
| 221 | + "uri": "pkg:github/trufflesecurity/trufflehog@466da4b", |
| 222 | + "version": "3.90.8" |
| 223 | + }, |
| 224 | + "config": { |
| 225 | + "profiles": [ |
| 226 | + { |
| 227 | + "profile": "Custom", |
| 228 | + "uri": "https://example.com/trufflehog_config.yml", |
| 229 | + "last_updated": "2025-06-04T19:25:00Z" |
| 230 | + }], |
| 231 | + "exclusions": ["excluded_files.txt"], |
| 232 | + "files": [ |
| 233 | + "<ResourceDescriptor(trufflehog_config.yml)>", |
| 234 | + "<ResourceDescriptor(excluded_files.txt)>" |
| 235 | + ], |
| 236 | + "full_command": "trufflehog --config=trugglehog_config.yml --no-update git file://. --exclude-paths='excluded_files.txt' --json > th.json" |
| 237 | + }, |
| 238 | + "result": "PASS", |
| 239 | + "output": ["<ResourceDescriptor(th.json)>"] |
| 240 | + } |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +### OpenSCAP |
| 245 | + |
| 246 | +```jsonc |
| 247 | +{ |
| 248 | + "_type": "https://in-toto.io/Statement/v1", |
| 249 | + "subject": [{ |
| 250 | + "name": "foo", |
| 251 | + "digest": { "sha256": "78ec328..." } |
| 252 | + }], |
| 253 | + "predicateType": "https://in-toto.io/attestation/tool-assessment/v0.1", |
| 254 | + "predicate": { |
| 255 | + "tool": { |
| 256 | + "name": "Openscap", |
| 257 | + "type": "STIG Compliance Scan", |
| 258 | + "uri": "pkg:github/OpenSCAP/openscap@e9b2a41", |
| 259 | + "version": "1.4.2" |
| 260 | + }, |
| 261 | + "config": { |
| 262 | + "profiles": [ |
| 263 | + { |
| 264 | + "profile": "Ubuntu", |
| 265 | + "uri": "https://example.com/1.3/xccdf_ubuntu_profile.xml", |
| 266 | + "last_updated": "2025-06-04T19:25:00Z", |
| 267 | + "version": "v1.3" |
| 268 | + }], |
| 269 | + "files": ["<ResourceDescriptor(xccdf_ubuntu_profile.xml)>"], |
| 270 | + "full_command": "oscap xccdf eval --profile Ubuntu --results xccdf-results.xml xccdf_ubuntu_profile.xml" |
| 271 | + }, |
| 272 | + "result": "PASS", |
| 273 | + "output": ["<ResourceDescriptor(xccdf-results.xml)>"], |
| 274 | + "summary": { |
| 275 | + "score": 98, |
| 276 | + "total": 214, |
| 277 | + "pass": 21, |
| 278 | + "fail": 2, |
| 279 | + "not_checked": 3, |
| 280 | + "not_applicable": 188 |
| 281 | + } |
| 282 | + } |
| 283 | +} |
| 284 | +``` |
| 285 | + |
| 286 | +## Changelog and Migrations |
0 commit comments