Skip to content

Commit 57cc645

Browse files
committed
chore(sarif): Bump go-sarif version
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
1 parent bd33918 commit 57cc645

5 files changed

Lines changed: 213 additions & 32 deletions

File tree

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ require (
8585
github.com/nats-io/nats.go v1.49.0
8686
github.com/open-policy-agent/opa v1.12.1
8787
github.com/openvex/go-vex v0.2.5
88+
github.com/owenrumney/go-sarif/v3 v3.3.0
8889
github.com/posthog/posthog-go v0.0.0-20240327112532-87b23fe11103
8990
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
9091
github.com/sigstore/cosign/v3 v3.0.4
@@ -294,6 +295,7 @@ require (
294295
github.com/xanzy/ssh-agent v0.3.3 // indirect
295296
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
296297
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
298+
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
297299
github.com/yashtewari/glob-intersection v0.2.0 // indirect
298300
github.com/yusufpapurcu/wmi v1.2.4 // indirect
299301
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
@@ -415,7 +417,6 @@ require (
415417
github.com/morikuni/aec v1.0.0 // indirect
416418
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
417419
github.com/opencontainers/go-digest v1.0.0 // indirect
418-
github.com/owenrumney/go-sarif v1.1.1
419420
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
420421
github.com/pkg/errors v0.9.1 // indirect
421422
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect

go.sum

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW
161161
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
162162
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
163163
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
164-
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
165164
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
166165
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
167166
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
@@ -595,7 +594,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
595594
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
596595
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
597596
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
598-
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
599597
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
600598
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
601599
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -1079,8 +1077,8 @@ github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc=
10791077
github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
10801078
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
10811079
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
1082-
github.com/owenrumney/go-sarif v1.1.1 h1:QNObu6YX1igyFKhdzd7vgzmw7XsWN3/6NMGuDzBgXmE=
1083-
github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U=
1080+
github.com/owenrumney/go-sarif/v3 v3.3.0 h1:p5oSxEV0uPWBRpAspTmwWr4t1YZyKUpdoFzSB7WE90A=
1081+
github.com/owenrumney/go-sarif/v3 v3.3.0/go.mod h1:72MaugkExDexbSauRuPq6BvUAAqAX0TwoNYMIQyZCMw=
10841082
github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
10851083
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
10861084
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
@@ -1355,8 +1353,6 @@ github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CP
13551353
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
13561354
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=
13571355
github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
1358-
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
1359-
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
13601356
github.com/wasilibs/go-re2 v1.9.0 h1:kjAd8qbNvV4Ve2Uf+zrpTCrDHtqH4dlsRXktywo73JQ=
13611357
github.com/wasilibs/go-re2 v1.9.0/go.mod h1:0sRtscWgpUdNA137bmr1IUgrRX0Su4dcn9AEe61y+yI=
13621358
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4=
@@ -1372,6 +1368,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMc
13721368
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
13731369
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
13741370
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
1371+
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
13751372
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
13761373
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
13771374
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
@@ -1398,7 +1395,6 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
13981395
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
13991396
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
14001397
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
1401-
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
14021398
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
14031399
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
14041400
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
@@ -1569,7 +1565,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
15691565
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
15701566
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
15711567
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
1572-
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
15731568
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
15741569
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
15751570
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -1719,7 +1714,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
17191714
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
17201715
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
17211716
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
1722-
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
17231717
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
17241718
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
17251719
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=

pkg/attestation/crafter/materials/sarif.go

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ package materials
1818
import (
1919
"context"
2020
"fmt"
21+
"os"
2122

2223
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2324
api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
2425
"github.com/chainloop-dev/chainloop/pkg/casclient"
25-
"github.com/owenrumney/go-sarif/sarif"
26+
sarif "github.com/owenrumney/go-sarif/v3/pkg/report/v210/sarif"
2627
"github.com/rs/zerolog"
2728
)
2829

@@ -44,14 +45,25 @@ func NewSARIFCrafter(materialSchema *schemaapi.CraftingSchema_Material, backend
4445

4546
func (i *SARIFCrafter) Craft(ctx context.Context, filepath string) (*api.Attestation_Material, error) {
4647
i.logger.Debug().Str("path", filepath).Msg("decoding SARIF file")
47-
doc, err := sarif.Open(filepath)
48-
// parse doesn't fail if the provided file is a valid JSON, but not a valid CSAF VEX file
48+
49+
content, err := os.ReadFile(filepath)
50+
if err != nil {
51+
i.logger.Debug().Err(err).Msg("error reading file")
52+
53+
return nil, fmt.Errorf("invalid SARIF file: %w", ErrInvalidMaterialType)
54+
}
55+
56+
// Sanitize literal control characters inside JSON strings.
57+
// Some tools (e.g. SonarQube) emit unescaped control chars that violate RFC 8259.
58+
content = SanitizeJSONControlChars(content)
59+
60+
doc, err := sarif.FromBytes(content)
4961
if err != nil || doc.Schema == "" {
5062
if err != nil {
5163
i.logger.Debug().Err(err).Msg("error decoding file")
5264
}
5365

54-
return nil, fmt.Errorf("invalid SARIF file: %w", ErrInvalidMaterialType)
66+
return nil, fmt.Errorf("invalid SARIF file (%v): %w", err, ErrInvalidMaterialType)
5567
}
5668

5769
m, err := uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
@@ -64,16 +76,88 @@ func (i *SARIFCrafter) Craft(ctx context.Context, filepath string) (*api.Attesta
6476
return m, nil
6577
}
6678

67-
func (i *SARIFCrafter) injectAnnotations(m *api.Attestation_Material, doc *sarif.Report) {
68-
// add vendor information
69-
if len(doc.Runs) > 0 {
70-
// assuming vendor from first run.
71-
m.Annotations = make(map[string]string)
72-
if doc.Runs[0].Tool.Driver.Name != "" {
73-
m.Annotations[AnnotationToolNameKey] = doc.Runs[0].Tool.Driver.Name
79+
// SanitizeJSONControlChars escapes literal control characters (U+0000–U+001F) found
80+
// inside JSON string values. RFC 8259 forbids unescaped control characters in strings,
81+
// but some tools (e.g. SonarQube) emit them. This function replaces them with valid
82+
// JSON escape sequences so that Go's encoding/json can parse the content.
83+
func SanitizeJSONControlChars(data []byte) []byte {
84+
hasControlChars := false
85+
for _, b := range data {
86+
if b < 0x20 {
87+
hasControlChars = true
88+
89+
break
7490
}
75-
if doc.Runs[0].Tool.Driver.Version != nil && *doc.Runs[0].Tool.Driver.Version != "" {
76-
m.Annotations[AnnotationToolVersionKey] = *doc.Runs[0].Tool.Driver.Version
91+
}
92+
93+
if !hasControlChars {
94+
return data
95+
}
96+
97+
// Walk the bytes tracking JSON string boundaries. Only allocate output when
98+
// we actually find a control character inside a string.
99+
var out []byte
100+
inString := false
101+
copyStart := 0
102+
103+
for i := 0; i < len(data); i++ {
104+
b := data[i]
105+
106+
if inString && b == '\\' && i+1 < len(data) {
107+
i++
108+
109+
continue
77110
}
111+
112+
if b == '"' {
113+
inString = !inString
114+
115+
continue
116+
}
117+
118+
if inString && b < 0x20 {
119+
if out == nil {
120+
out = make([]byte, 0, len(data)+64)
121+
}
122+
123+
out = append(out, data[copyStart:i]...)
124+
125+
switch b {
126+
case '\n':
127+
out = append(out, '\\', 'n')
128+
case '\r':
129+
out = append(out, '\\', 'r')
130+
case '\t':
131+
out = append(out, '\\', 't')
132+
default:
133+
out = append(out, fmt.Sprintf("\\u%04x", b)...)
134+
}
135+
136+
copyStart = i + 1
137+
}
138+
}
139+
140+
if out == nil {
141+
return data
142+
}
143+
144+
out = append(out, data[copyStart:]...)
145+
146+
return out
147+
}
148+
149+
func (i *SARIFCrafter) injectAnnotations(m *api.Attestation_Material, doc *sarif.Report) {
150+
if len(doc.Runs) == 0 {
151+
return
152+
}
153+
154+
m.Annotations = make(map[string]string)
155+
driver := doc.Runs[0].Tool.Driver
156+
157+
if driver.Name != nil && *driver.Name != "" {
158+
m.Annotations[AnnotationToolNameKey] = *driver.Name
159+
}
160+
if driver.Version != nil && *driver.Version != "" {
161+
m.Annotations[AnnotationToolVersionKey] = *driver.Version
78162
}
79163
}

pkg/attestation/crafter/materials/sarif_test.go

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ func TestNewSARIFCrafter(t *testing.T) {
6666

6767
func TestSARIFCraft(t *testing.T) {
6868
testCases := []struct {
69-
name string
70-
filePath string
71-
wantErr string
69+
name string
70+
filePath string
71+
wantErr string
72+
expectedDigest string
73+
expectedName string
7274
}{
7375
{
7476
name: "non-expected json file",
@@ -86,8 +88,14 @@ func TestSARIFCraft(t *testing.T) {
8688
wantErr: "unexpected material type",
8789
},
8890
{
89-
name: "valid artifact type",
90-
filePath: "./testdata/report.sarif",
91+
name: "valid artifact type",
92+
filePath: "./testdata/report.sarif",
93+
expectedDigest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95",
94+
expectedName: "report.sarif",
95+
},
96+
{
97+
name: "valid SARIF with literal control characters in JSON strings",
98+
filePath: "./testdata/report-control-chars.sarif",
9199
},
92100
}
93101

@@ -123,10 +131,62 @@ func TestSARIFCraft(t *testing.T) {
123131
assert.Equal(contractAPI.CraftingSchema_Material_SARIF.String(), got.MaterialType.String())
124132
assert.True(got.UploadedToCas)
125133

126-
// // The result includes the digest reference
127-
assert.Equal(&attestationApi.Attestation_Material_Artifact{
128-
Id: "test", Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", Name: "report.sarif",
129-
}, got.GetArtifact())
134+
if tc.expectedDigest != "" {
135+
assert.Equal(&attestationApi.Attestation_Material_Artifact{
136+
Id: "test", Digest: tc.expectedDigest, Name: tc.expectedName,
137+
}, got.GetArtifact())
138+
}
139+
})
140+
}
141+
}
142+
143+
func TestSanitizeJSONControlChars(t *testing.T) {
144+
testCases := []struct {
145+
name string
146+
input []byte
147+
expected []byte
148+
}{
149+
{
150+
name: "no control chars — returned as-is",
151+
input: []byte(`{"key": "value"}`),
152+
expected: []byte(`{"key": "value"}`),
153+
},
154+
{
155+
name: "literal CR+LF inside string",
156+
input: []byte("{\"seq\": \"\r\n\"}"),
157+
expected: []byte(`{"seq": "\r\n"}`),
158+
},
159+
{
160+
name: "literal LF inside string",
161+
input: []byte("{\"seq\": \"\n\"}"),
162+
expected: []byte(`{"seq": "\n"}`),
163+
},
164+
{
165+
name: "literal tab inside string",
166+
input: []byte("{\"seq\": \"\t\"}"),
167+
expected: []byte(`{"seq": "\t"}`),
168+
},
169+
{
170+
name: "null byte inside string",
171+
input: []byte("{\"seq\": \"\x00\"}"),
172+
expected: []byte(`{"seq": "\u0000"}`),
173+
},
174+
{
175+
name: "already-escaped sequences are preserved",
176+
input: []byte(`{"seq": "\\r\\n"}`),
177+
expected: []byte(`{"seq": "\\r\\n"}`),
178+
},
179+
{
180+
name: "newlines outside strings are not modified",
181+
input: []byte("{\n \"key\": \"val\"\n}"),
182+
expected: []byte("{\n \"key\": \"val\"\n}"),
183+
},
184+
}
185+
186+
for _, tc := range testCases {
187+
t.Run(tc.name, func(t *testing.T) {
188+
got := materials.SanitizeJSONControlChars(tc.input)
189+
assert.Equal(t, tc.expected, got)
130190
})
131191
}
132192
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json",
3+
"version": "2.1.0",
4+
"runs": [
5+
{
6+
"tool": {
7+
"driver": {
8+
"name": "SonarQube",
9+
"version": "2025.5.0",
10+
"informationUri": "https://sonarqube.example.com"
11+
}
12+
},
13+
"results": [
14+
{
15+
"ruleId": "test:S001",
16+
"level": "warning",
17+
"message": {
18+
"text": "Test finding"
19+
},
20+
"locations": [
21+
{
22+
"physicalLocation": {
23+
"artifactLocation": {
24+
"uri": "test.xml"
25+
},
26+
"region": {
27+
"startLine": 1
28+
}
29+
}
30+
}
31+
]
32+
}
33+
],
34+
"newlineSequences": [
35+
"
36+
",
37+
"
38+
"
39+
]
40+
}
41+
]
42+
}

0 commit comments

Comments
 (0)