mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 05:40:11 +01:00
Extend SHA marking tests
This commit is contained in:
parent
13c94f4fa0
commit
ddb5518c6d
14 changed files with 143 additions and 46 deletions
|
|
@ -44,8 +44,8 @@ const (
|
||||||
type hashAlgorithm string
|
type hashAlgorithm string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
algSha256 = hashAlgorithm("SHA256")
|
algSha256 = hashAlgorithm("sha256")
|
||||||
algSha2512 = hashAlgorithm("SHA512")
|
algSha512 = hashAlgorithm("sha512")
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ nextAdvisory:
|
||||||
signData []byte
|
signData []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
if (d.cfg.PreferredHash != "sha512" || file.SHA512URL() == "") && file.SHA256URL() != "" {
|
if (d.cfg.PreferredHash != algSha512 || file.SHA512URL() == "") && file.SHA256URL() != "" {
|
||||||
// Only hash when we have a remote counterpart we can compare it with.
|
// Only hash when we have a remote counterpart we can compare it with.
|
||||||
if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil {
|
if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil {
|
||||||
if !file.IsDirectory() {
|
if !file.IsDirectory() {
|
||||||
|
|
@ -518,7 +518,7 @@ nextAdvisory:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.cfg.PreferredHash != "sha256" || file.SHA256URL() == "") && file.SHA512URL() != "" {
|
if (d.cfg.PreferredHash != algSha256 || file.SHA256URL() == "") && file.SHA512URL() != "" {
|
||||||
if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil {
|
if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil {
|
||||||
if !file.IsDirectory() {
|
if !file.IsDirectory() {
|
||||||
slog.Warn("Cannot fetch SHA512",
|
slog.Warn("Cannot fetch SHA512",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -53,6 +54,12 @@ func ProviderHandler(params *ProviderParams, directoryProvider bool) http.Handle
|
||||||
w.Header().Add("Content-Type", "text/html")
|
w.Header().Add("Content-Type", "text/html")
|
||||||
case strings.HasSuffix(path, ".json"):
|
case strings.HasSuffix(path, ".json"):
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
case strings.HasSuffix(path, ".sha512") && directoryProvider && !params.EnableSha512:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
default:
|
default:
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
}
|
}
|
||||||
|
|
@ -62,22 +69,97 @@ func ProviderHandler(params *ProviderParams, directoryProvider bool) http.Handle
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmplt.Execute(w, params)
|
err = tmplt.Execute(w, params)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkIfFileExists(path string, t *testing.T) bool {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return true
|
||||||
|
} else if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Failed to check if file exists: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShaMarking(t *testing.T) {
|
func TestShaMarking(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
directoryProvider bool
|
directoryProvider bool
|
||||||
wantSha256 bool
|
wantSha256 bool
|
||||||
wantSha512 bool
|
wantSha512 bool
|
||||||
|
enableSha256 bool
|
||||||
|
enableSha512 bool
|
||||||
|
preferredHash hashAlgorithm
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "want sha256 and sha512",
|
name: "want sha256 and sha512",
|
||||||
directoryProvider: false,
|
directoryProvider: false,
|
||||||
wantSha256: true,
|
wantSha256: true,
|
||||||
wantSha512: true,
|
wantSha512: true,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only want sha256",
|
||||||
|
directoryProvider: false,
|
||||||
|
wantSha256: true,
|
||||||
|
wantSha512: false,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
preferredHash: algSha256,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only want sha512",
|
||||||
|
directoryProvider: false,
|
||||||
|
wantSha256: false,
|
||||||
|
wantSha512: true,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
preferredHash: algSha512,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only want sha512",
|
||||||
|
directoryProvider: false,
|
||||||
|
wantSha256: false,
|
||||||
|
wantSha512: true,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
preferredHash: algSha512,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "only deliver sha256",
|
||||||
|
directoryProvider: false,
|
||||||
|
wantSha256: true,
|
||||||
|
wantSha512: false,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: false,
|
||||||
|
preferredHash: algSha512,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only want sha256, directory provider",
|
||||||
|
directoryProvider: true,
|
||||||
|
wantSha256: true,
|
||||||
|
wantSha512: false,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
preferredHash: algSha256,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only want sha512, directory provider",
|
||||||
|
directoryProvider: true,
|
||||||
|
wantSha256: false,
|
||||||
|
wantSha512: true,
|
||||||
|
enableSha256: true,
|
||||||
|
enableSha512: true,
|
||||||
|
preferredHash: algSha512,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,8 +171,8 @@ func TestShaMarking(t *testing.T) {
|
||||||
serverURL := ""
|
serverURL := ""
|
||||||
params := ProviderParams{
|
params := ProviderParams{
|
||||||
URL: "",
|
URL: "",
|
||||||
EnableSha256: true,
|
EnableSha256: test.enableSha256,
|
||||||
EnableSha512: true,
|
EnableSha512: test.enableSha512,
|
||||||
}
|
}
|
||||||
server := httptest.NewTLSServer(ProviderHandler(¶ms, test.directoryProvider))
|
server := httptest.NewTLSServer(ProviderHandler(¶ms, test.directoryProvider))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
@ -102,19 +184,34 @@ func TestShaMarking(t *testing.T) {
|
||||||
client := util.Client(hClient)
|
client := util.Client(hClient)
|
||||||
|
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
cfg := config{LogLevel: &options.LogLevel{Level: slog.LevelDebug}, Directory: tempDir}
|
cfg := config{LogLevel: &options.LogLevel{Level: slog.LevelDebug}, Directory: tempDir, PreferredHash: test.preferredHash}
|
||||||
cfg.prepare()
|
err := cfg.prepare()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SHA marking config failed: %v", err)
|
||||||
|
}
|
||||||
d, err := newDownloader(&cfg)
|
d, err := newDownloader(&cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not init downloader: %v", err)
|
t.Fatalf("could not init downloader: %v", err)
|
||||||
}
|
}
|
||||||
defer d.close()
|
|
||||||
d.client = &client
|
d.client = &client
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err = d.run(ctx, []string{serverURL + "/provider-metadata.json"})
|
err = d.run(ctx, []string{serverURL + "/provider-metadata.json"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("SHA marking: Expected no error, got: %v", err)
|
t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err)
|
||||||
|
}
|
||||||
|
d.close()
|
||||||
|
|
||||||
|
// Check for downloaded hashes
|
||||||
|
sha256Exists := checkIfFileExists(tempDir+"/white/2020/avendor-advisory-0004.json.sha256", t)
|
||||||
|
sha512Exists := checkIfFileExists(tempDir+"/white/2020/avendor-advisory-0004.json.sha512", t)
|
||||||
|
|
||||||
|
if sha256Exists != test.wantSha256 {
|
||||||
|
t.Errorf("%v: expected sha256 hash present to be %v, got: %v", test.name, test.wantSha256, sha256Exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sha512Exists != test.wantSha512 {
|
||||||
|
t.Errorf("%v: expected sha512 hash present to be %v, got: %v", test.name, test.wantSha512, sha512Exists)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
{
|
{
|
||||||
"canonical_url": "/provider-metadata.json",
|
"canonical_url": "{{.URL}}/provider-metadata.json",
|
||||||
"distributions": [
|
"distributions": [
|
||||||
{
|
{
|
||||||
"directory_url": "/white/"
|
"directory_url": "{{.URL}}/white/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"last_updated": "2020-00-00T00:00:00Z",
|
"last_updated": "2020-01-01T00:00:00Z",
|
||||||
"list_on_CSAF_aggregators": true,
|
"list_on_CSAF_aggregators": true,
|
||||||
"metadata_version": "2.0",
|
"metadata_version": "2.0",
|
||||||
"mirror_on_CSAF_aggregators": true,
|
"mirror_on_CSAF_aggregators": true,
|
||||||
"public_openpgp_keys": [
|
"public_openpgp_keys": [
|
||||||
{
|
{
|
||||||
"fingerprint": "A8914CA2F11139C6A69A0018FB3CD9B15DE61596",
|
"fingerprint": "A8914CA2F11139C6A69A0018FB3CD9B15DE61596",
|
||||||
"url": "/openpgp/pubkey.asc"
|
"url": "{{.URL}}/openpgp/pubkey.asc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"publisher": {
|
"publisher": {
|
||||||
|
|
|
||||||
|
|
@ -22,19 +22,19 @@
|
||||||
},
|
},
|
||||||
"title": "Test CSAF document",
|
"title": "Test CSAF document",
|
||||||
"tracking": {
|
"tracking": {
|
||||||
"current_release_date": "2020-00-00T00:00:00Z",
|
"current_release_date": "2020-01-01T00:00:00Z",
|
||||||
"generator": {
|
"generator": {
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"engine": {
|
"engine": {
|
||||||
"name": "csaf-tool",
|
"name": "csaf-tool",
|
||||||
"version": "0.3.2"
|
"version": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "Avendor-advisory-0004",
|
"id": "Avendor-advisory-0004",
|
||||||
"initial_release_date": "2020-00-00T00:00:00Z",
|
"initial_release_date": "2020-01-01T00:00:00Z",
|
||||||
"revision_history": [
|
"revision_history": [
|
||||||
{
|
{
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"summary": "Initial version"
|
"summary": "Initial version"
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Customers should upgrade to the latest version of the product",
|
"details": "Customers should upgrade to the latest version of the product",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Still under investigation",
|
"details": "Still under investigation",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -22,19 +22,19 @@
|
||||||
},
|
},
|
||||||
"title": "Test CSAF document",
|
"title": "Test CSAF document",
|
||||||
"tracking": {
|
"tracking": {
|
||||||
"current_release_date": "2020-00-00T00:00:00Z",
|
"current_release_date": "2020-01-01T00:00:00Z",
|
||||||
"generator": {
|
"generator": {
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"engine": {
|
"engine": {
|
||||||
"name": "csaf-tool",
|
"name": "csaf-tool",
|
||||||
"version": "0.3.2"
|
"version": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "Avendor-advisory-0004",
|
"id": "Avendor-advisory-0004",
|
||||||
"initial_release_date": "2020-00-00T00:00:00Z",
|
"initial_release_date": "2020-01-01T00:00:00Z",
|
||||||
"revision_history": [
|
"revision_history": [
|
||||||
{
|
{
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"summary": "Initial version"
|
"summary": "Initial version"
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Customers should upgrade to the latest version of the product",
|
"details": "Customers should upgrade to the latest version of the product",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Still under investigation",
|
"details": "Still under investigation",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZuftaAAKCRD7PNmxXeYV
|
iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZukv9QAKCRD7PNmxXeYV
|
||||||
lh/FAP90NaBzCYu9JpIuqvG8MZegVLpc85P6AeeMPU5W6dRHJwD/eQzrfy0TzAZ2
|
ljq0AP9n/rTgoNCJzSTZzNrrMy28ZR+Ppp1MSPWGFUzsx6qLJgD/d8cu0lokMsXf
|
||||||
q6xmpih4scq9j8frOjoH40w3rAByoQc=
|
y0uc9k7hrla/ajFUzNt3AVvT+CPFtAo=
|
||||||
=EhZh
|
=7E66
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
ab1995c322f4b7905b79d78646fd8774900e27ac0b00128e892cd84cee1f17e2 avendor-advisory-0004.json
|
cb263bf1beab18b893de63f2966d0d8c5f38d60101c24d3fd7a5feebaad02c3b avendor-advisory-0004.json
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
8a7dd7d1247f510a59c26bf6f973ff4c3a8d37c5a69fd361aa7cc7cf06a38a8ef096e4f2f09b75269ad2b1cc99179381a63c7c731e5e492a9647ba0452255b37 avendor-advisory-0004.json
|
39476e1d08a0871d166091c90de259544382a3599eebda118a93468499a30fd034286086c461a97d3d5298e093b0be3868e8d89d8a6a255c4aa6adb81ebbfcad avendor-advisory-0004.json
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
"avendor-advisory-0004.json","2020-00-00T00:00:00+00:00"
|
"avendor-advisory-0004.json","2020-01-01T00:00:00+00:00"
|
||||||
|
|
|
||||||
|
|
|
@ -22,19 +22,19 @@
|
||||||
},
|
},
|
||||||
"title": "Test CSAF document",
|
"title": "Test CSAF document",
|
||||||
"tracking": {
|
"tracking": {
|
||||||
"current_release_date": "2020-00-00T00:00:00Z",
|
"current_release_date": "2020-01-01T00:00:00Z",
|
||||||
"generator": {
|
"generator": {
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"engine": {
|
"engine": {
|
||||||
"name": "csaf-tool",
|
"name": "csaf-tool",
|
||||||
"version": "0.3.2"
|
"version": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "Avendor-advisory-0004",
|
"id": "Avendor-advisory-0004",
|
||||||
"initial_release_date": "2020-00-00T00:00:00Z",
|
"initial_release_date": "2020-01-01T00:00:00Z",
|
||||||
"revision_history": [
|
"revision_history": [
|
||||||
{
|
{
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"summary": "Initial version"
|
"summary": "Initial version"
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Customers should upgrade to the latest version of the product",
|
"details": "Customers should upgrade to the latest version of the product",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
{
|
{
|
||||||
"category": "impact",
|
"category": "impact",
|
||||||
"details": "Still under investigation",
|
"details": "Still under investigation",
|
||||||
"date": "2020-00-00T00:00:00Z",
|
"date": "2020-01-01T00:00:00Z",
|
||||||
"product_ids": ["CSAFPID_0001"]
|
"product_ids": ["CSAFPID_0001"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZuftaAAKCRD7PNmxXeYV
|
iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZukv9QAKCRD7PNmxXeYV
|
||||||
lh/FAP90NaBzCYu9JpIuqvG8MZegVLpc85P6AeeMPU5W6dRHJwD/eQzrfy0TzAZ2
|
ljq0AP9n/rTgoNCJzSTZzNrrMy28ZR+Ppp1MSPWGFUzsx6qLJgD/d8cu0lokMsXf
|
||||||
q6xmpih4scq9j8frOjoH40w3rAByoQc=
|
y0uc9k7hrla/ajFUzNt3AVvT+CPFtAo=
|
||||||
=EhZh
|
=7E66
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
ab1995c322f4b7905b79d78646fd8774900e27ac0b00128e892cd84cee1f17e2 avendor-advisory-0004.json
|
cb263bf1beab18b893de63f2966d0d8c5f38d60101c24d3fd7a5feebaad02c3b avendor-advisory-0004.json
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
8a7dd7d1247f510a59c26bf6f973ff4c3a8d37c5a69fd361aa7cc7cf06a38a8ef096e4f2f09b75269ad2b1cc99179381a63c7c731e5e492a9647ba0452255b37 avendor-advisory-0004.json
|
39476e1d08a0871d166091c90de259544382a3599eebda118a93468499a30fd034286086c461a97d3d5298e093b0be3868e8d89d8a6a255c4aa6adb81ebbfcad avendor-advisory-0004.json
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue