1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 18:15:42 +01:00

Compare commits

...

87 commits
v3.2.0 ... main

Author SHA1 Message Date
Sascha L. Teichmann
586524a97e
Update 3rd party libraries. (#711)
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
2025-12-18 13:25:44 +01:00
Benjamin Grandfond
52ce6bcde6
fix: engine is invalid when name is missing (#710)
Some checks failed
generate-markdown / auto-update-readme (push) Waiting to run
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-12-18 12:50:37 +01:00
JanHoefelmeyer
9393271699
Merge pull request #703 from gocsaf/add_rolie_category
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
ROLIE: Add category to entries
2025-12-01 07:14:49 +01:00
JanHoefelmeyer
0630a9a64a
Merge pull request #706 from gocsaf/3rdparty_updates_2025_11_28
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Update 3rd party libraries
2025-11-28 16:14:48 +01:00
JanHoefelmeyer
502376ce3a fix typo: contibutor -> contributor
Some checks failed
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-11-28 16:12:10 +01:00
Sascha L. Teichmann
c678a97d43 Update 3rd party libraries 2025-11-28 11:03:29 +01:00
Sascha L. Teichmann
9a37a8ecfa Add more fields to rolie entry.
Some checks are pending
Go Test (oldstable) / build (push) Waiting to run
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
2025-11-27 15:23:34 +01:00
Sascha L. Teichmann
d6bac95e45 Removed debugging code 2025-11-19 12:56:04 +01:00
Sascha L. Teichmann
5a1c2a0873 Add category field to ROLIE feed model. 2025-11-19 12:12:43 +01:00
JanHoefelmeyer
8dd4cb4fa8
Merge pull request #696 from gocsaf/slient-revive
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
silence revive linter warnings
2025-10-27 11:07:06 +01:00
Christoph Klassen
9607f8db94
fix: Documentation about supported options (#697) 2025-10-27 10:38:22 +01:00
Bernhard E. Reiter
46118544be
upgrade dependencies, including go (#695)
* change go.mod as first step towards go 1.25

  raise minium version of go compatiblity to 1.24.9
  and toolchain to be used to 1.25.3

* upgrade .github/workflows and documentation

  * update all .github/workflows/ to use the latest version of
    actions and the go versions accordingly.
    (Only some github actions use a floating tag for the major version.)
  * reduce places where the go versions are hardcoded:
      * refEr to docs/Development.md from README.md
      * use `go.mod` from itest.yml.

* fix itest.yml: checkout before refer to go.mod

* improve code cleanness: use format string w error

  and thus makes newer go test versions happy

* update go dependencies

* cleanup some dependencies with go mod tidy

* fix .github/workflows action versions

* fix go action versions
2025-10-27 10:35:38 +01:00
Bernhard Reiter
fb59a40609
fix code formatting 2025-10-23 16:19:13 +02:00
Bernhard Reiter
cf9c62fcc0
silence revive linter warnings
that we cannot or do not want to fix yet
2025-10-23 16:09:18 +02:00
Bernhard Reiter
b6281012f5
fix go action versions 2025-10-23 14:15:32 +02:00
Bernhard Reiter
8740244dd8
fix .github/workflows action versions 2025-10-23 14:12:16 +02:00
Bernhard Reiter
6cc1d7a38f
cleanup some dependencies with go mod tidy 2025-10-23 13:55:14 +02:00
Bernhard Reiter
ffb1a31944
update go dependencies 2025-10-23 13:22:37 +02:00
Bernhard Reiter
ef44c92f8b
improve code cleanness: use format string w error
and thus makes newer go test versions happy
2025-10-23 13:17:28 +02:00
Bernhard Reiter
223570ac9b
fix itest.yml: checkout before refer to go.mod 2025-10-23 13:00:33 +02:00
Bernhard Reiter
fc012fa820
upgrade .github/workflows and documentation
* update all .github/workflows/ to use the latest version of
    actions and the go versions accordingly.
    (Only some github actions use a floating tag for the major version.)
  * reduce places where the go versions are hardcoded:
      * refEr to docs/Development.md from README.md
      * use `go.mod` from itest.yml.
2025-10-23 12:42:36 +02:00
Bernhard Reiter
f046ade489
change go.mod as first step towards go 1.25
raise minium version of go compatiblity to 1.24.9
  and toolchain to be used to 1.25.3
2025-10-23 12:09:41 +02:00
Bernhard E. Reiter
c6bad42c24
Improve LoadCertificate unit test (#692)
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
* fix `LoadCertificate` unit test

replaced certificate with invalid dns name, which is rejected by stdlib of Go version >=1.25.2.

Change in Go introduced by https://github.com/golang/go/issues/75715

* code review: add script to generate certificates, remove `greenbone` org entry

* code review: add license header

* rework cert creation and fix one filename

---------

Co-authored-by: Marius Goetze <marius.goetze@greenbone.net>
2025-10-22 16:57:00 +02:00
JanHoefelmeyer
05eae0a9ae
Re-add unknown fields check (#681)
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go Test (oldstable) / build (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-10-01 11:14:09 +02:00
Christoph Klassen
e3d2a58528
Merge pull request #680 from gocsaf/better-workflow-name
Rename workflow go_legacy to "Go Test (oldstable)"
2025-10-01 10:59:44 +02:00
JanHoefelmeyer
04955d6fad
Merge pull request #678 from greenbone/fix-formatted-string-upstream
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
fix incorrect usage of formatted string
2025-09-22 17:04:02 +02:00
mgoetzegb
0dbf822cbd
fix doc comment: remove untrue claim of disallowing unknown fields (#677)
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
adjust comment to fit 7935818600
2025-09-15 12:42:30 +02:00
Bernhard Reiter
bcb7c8be10
rename go_legacy.yml -> go-oldstable.yml 2025-09-12 11:41:13 +02:00
Bernhard E. Reiter
5c1b061255
Rename workflow go_legacy to "Go Test (oldstable)"
so it is distinct from the other "Go" workflow
2025-09-12 11:38:56 +02:00
Marius Goetze
d1f33ab27d fix incorrect usage of formatted string
output probably unchanged, but now `go vet` is happy that formatted strings are not misused
2025-09-08 13:19:15 +02:00
Paul Schwabauer
187d114631
Remove unnecessary URL joins (#676)
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
This should avoid bugs for more complex scenarios.
2025-09-01 16:13:57 +02:00
Bernhard E. Reiter
1a2a8fae9c
improve docs (minor) for csaf_provider (#668)
Some checks are pending
generate-markdown / auto-update-readme (push) Waiting to run
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
* add a "both" to explain the config file option
    `certificate_and_password` better.
2025-09-01 15:40:42 +02:00
Bernhard E. Reiter
f6927154bf
improve calculated version numbers (#651)
for modified git workspaces a `-modified` is added to the semantic version
  in the makefile.
2025-09-01 15:40:26 +02:00
Paul Schwabauer
1f1a2a4cbc
Add arm64 builds for windows and linux (#663) 2025-09-01 12:04:17 +02:00
JanHoefelmeyer
fa8370bd60
Merge pull request #675 from gocsaf/doc18
improve docs/csaf_downloader.md (minor) time_range
2025-09-01 11:53:15 +02:00
JanHoefelmeyer
7ab964a3e3
Doc: Highlight the reason for the rate options existence (#662)
* Doc: Highlight the reason for the rate options existence

* Fix typos
2025-09-01 11:48:56 +02:00
JanHoefelmeyer
c7a284bf7f
Merge pull request #667 from gocsaf/docs-10
fix minor docs typo
2025-09-01 11:45:37 +02:00
Christoph Klassen
08ab318545
Merge pull request #674 from gocsaf/fix-listing-check
Fix csaf checker listed check
2025-09-01 11:15:24 +02:00
Christoph Klassen
a2fab16d3b
Merge pull request #671 from gocsaf/fix-create-error-handling
Fix #669
2025-09-01 11:13:56 +02:00
Bernhard Reiter
108e5f8620
improve docs/csaf_downloader.md (minor) time_range 2025-08-26 15:24:51 +02:00
koplas
100e4d395b Fix csaf checker listed check
Correctly handle URLs that are absolute.
2025-08-26 11:58:49 +02:00
koplas
7fc5600521
Fix #669
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Return error when the create request failed.
2025-08-11 08:50:02 +02:00
Sebastian Wagner
7f27a63e3c
docs provider-setup.md: Fix create URL in curl command
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
2025-08-01 11:42:52 +02:00
Bernhard Reiter
230e9f2d2b
fix minor docs typo 2025-07-31 11:29:44 +02:00
JanHoefelmeyer
ae184eb189
Merge pull request #655 from gocsaf/json-eof
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Make json parsing more strict
2025-07-08 07:46:07 +02:00
JanHoefelmeyer
4b4d6ed594 Remove uknown field tests
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-07-07 11:45:36 +02:00
JanHoefelmeyer
7935818600 Fix: Allow unknown fields: They are not forbidden 2025-07-07 11:41:49 +02:00
koplas
c81f55a752
Add LoadAdvisory tests
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-07-04 15:29:03 +02:00
JanHoefelmeyer
e7c08d05cd Rewrite function from scratch
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-07-03 11:03:06 +02:00
koplas
fc3837d655
Make json parsing more strict
Some checks are pending
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
2025-07-02 17:06:25 +02:00
Christoph Klassen
dad4e54184
Merge pull request #659 from gocsaf/fix-checker-join
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Fix checker url base handling
2025-07-02 16:34:13 +02:00
koplas
01c43d96ce
Fix checker url base handling 2025-07-02 16:27:58 +02:00
Christoph Klassen
ca54ba53be
Merge pull request #658 from gocsaf/fix-agg-join
Fix aggregator url base handling
2025-07-02 15:53:33 +02:00
koplas
3262e2ec2a
Fix aggregator url base handling 2025-07-02 15:33:37 +02:00
Christoph Klassen
bcd34d9fba
Merge pull request #653 from gocsaf/top-level-docs
Some checks are pending
generate-markdown / auto-update-readme (push) Waiting to run
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
docs: improve README.md
2025-07-02 09:38:22 +02:00
Christoph Klassen
5fd5076f52
Merge pull request #656 from gocsaf/warn-config
Print warning if no config file was found
2025-07-02 09:33:58 +02:00
JanHoefelmeyer
21ce19735b Fix: Fix typo and misleading meaning 2025-07-02 09:23:23 +02:00
JanHoefelmeyer
27e9519ed5 Fix: Remove some Typos as well as grammatical errors and oddities 2025-07-02 09:20:27 +02:00
koplas
a7b1291be8
Print warning if no config file was found 2025-06-27 17:20:19 +02:00
Bernhard Reiter
7b7d0c4dcb
improve phrasing
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-06-27 10:24:48 +02:00
Bernhard Reiter
a6d0a0c790
docs: extend package csaf doc comment
* fix sentence.
  * add link to the section in the top-level readme that has the limits
    on the use as a library.
2025-06-27 10:20:56 +02:00
Bernhard Reiter
d54e211ef3
docs: improve README.md
* Deemphazise the old repo link alert.
 * Add more hints about officially unsupported but possible use as
   library.

solve #634
2025-06-27 09:49:32 +02:00
Christoph Klassen
c833c00f84
Merge pull request #649 from gocsaf/url-join
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Use JoinPath
2025-06-26 08:18:39 +02:00
Christoph Klassen
4066704c1a
Merge pull request #633 from gocsaf/check-prefix-url
Some checks are pending
generate-markdown / auto-update-readme (push) Waiting to run
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
Check if canonical url prefix is valid
2025-06-25 17:05:09 +02:00
Christoph Klassen
f154b78340
Merge pull request #652 from gocsaf/less_bloat
Feat: More explicitely handle which doc files are included in the dist
2025-06-25 15:37:45 +02:00
JanHoefelmeyer
d5778f0755
Merge pull request #647 from gocsaf/pmd-diagnostic
csaf_checker: Always generate report
2025-06-25 15:33:47 +02:00
JanHoefelmeyer
5d37dd1339 Move PMD error from logs to report.
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-06-25 09:31:50 +02:00
JanHoefelmeyer
d09db6635d Fix: Assume most restrictive role to prevent false-positives
Some checks are pending
Go / build (push) Waiting to run
Go / run_modver (push) Blocked by required conditions
2025-06-24 17:24:08 +02:00
koplas
3f4fe5cf18
Also generate report when role is not available 2025-06-24 17:18:42 +02:00
JanHoefelmeyer
02d4931152 Fix: Return properly early 2025-06-24 17:06:55 +02:00
JanHoefelmeyer
9c62e89a23 Feat: More explicitely handle which doc files are included in the gnulinux dist 2025-06-24 14:34:44 +02:00
Christoph Klassen
062e145761
Merge pull request #650 from gocsaf/write-version
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Use folder name as version if git describe failed
2025-06-24 10:48:11 +02:00
koplas
36aab33de4 Use folder name as version if git describe failed 2025-06-20 16:50:13 +02:00
koplas
1098c6add0 Use correct base URL
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
2025-06-20 16:37:37 +02:00
koplas
091854a248 Always generate report
Some checks failed
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Closes #385
2025-06-20 14:24:05 +02:00
Christoph Klassen
ce886f138a
Merge pull request #648 from gocsaf/update-modver
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Update modver
2025-06-20 08:59:50 +02:00
koplas
6ac97810d0
Use JoinPath
This avoids issues where parts of the URL are discarded.
2025-06-19 15:11:45 +02:00
koplas
cb291bb81b
Update modver 2025-06-19 14:39:02 +02:00
Christoph Klassen
12cde3aa3c
Merge pull request #637 from gocsaf/api-break-action
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Go / build (push) Has been cancelled
Go / run_modver (push) Has been cancelled
Add semver breaking changes detection
2025-06-18 09:04:37 +02:00
Christoph Klassen
fa1861385a
Merge pull request #643 from gocsaf/jsonschema-upgrade
Upgrade jsonschema to v6
2025-06-18 08:51:06 +02:00
koplas
dcdbc5d49d
Add semver breaking changes detection 2025-06-13 18:50:57 +02:00
koplas
34705f3c6e Address comments
Some checks failed
Go / build (push) Has been cancelled
2025-06-13 11:01:43 +02:00
koplas
6955c4e37c Upgrade node.js and format workflow file 2025-06-13 10:19:21 +02:00
koplas
fc64bf7165
Upgrade jsonschema to v6 2025-06-12 15:53:39 +02:00
JanHoefelmeyer
161ec1f15c
Merge pull request #635 from gocsaf/remove-golint-action
Some checks failed
generate-markdown / auto-update-readme (push) Has been cancelled
Remove golint github action
2025-06-10 07:45:56 +02:00
Christoph Klassen
3ab00e8759
Remove golint github action
We use Revive already which is a replacement for golint and golint isn't maintained anyway.
2025-05-28 11:30:46 +02:00
koplas
91b5b4543e
Check if canonical url prefix is valid 2025-04-03 14:41:14 +02:00
56 changed files with 1399 additions and 575 deletions

View file

@ -13,8 +13,8 @@ jobs:
auto-update-readme: auto-update-readme:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v5
- name: Markdown autodocs - name: Markdown autodocs
uses: dineshsonachalam/markdown-autodocs@v1.0.4 uses: dineshsonachalam/markdown-autodocs@v1.0.7
with: with:
output_file_paths: '[./README.md, ./docs/*.md]' output_file_paths: '[./README.md, ./docs/*.md]'

View file

@ -1,4 +1,4 @@
name: Go name: Go Test (oldstable)
on: on:
push: push:
@ -12,10 +12,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v4 uses: actions/setup-go@v6
with: with:
go-version: 'oldstable' go-version: 'oldstable'

View file

@ -12,10 +12,11 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout
uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v4 uses: actions/setup-go@v6
with: with:
go-version: "stable" go-version: "stable"
@ -26,15 +27,36 @@ jobs:
run: go vet ./... run: go vet ./...
- name: gofmt - name: gofmt
uses: Jerome1337/gofmt-action@v1.0.4 uses: Jerome1337/gofmt-action@v1.0.5
with: with:
gofmt-flags: "-l -d" gofmt-flags: "-l -d"
- name: golint
uses: Jerome1337/golint-action@v1.0.3
- name: Revive Action - name: Revive Action
uses: morphy2k/revive-action@v2.7.4 uses: morphy2k/revive-action@v2
- name: Tests - name: Tests
run: go test -v ./... run: go test -v ./...
run_modver:
runs-on: ubuntu-latest
needs: build # Only run when build job was successful
if: ${{ github.event_name == 'pull_request' && success() }}
permissions:
contents: read # Modver needs to read the repo content
pull-requests: write # Modver needs to write comments/status on PRs
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0 # Modver needs full history for comparison
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "stable"
- name: Modver
uses: bobg/modver@v2.12.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pull_request_url: https://github.com/${{ github.repository }}/pull/${{ github.event.number }}

View file

@ -5,19 +5,19 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version: '^1.23.6' go-version-file: "go.mod"
check-latest: true
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v6
with: with:
node-version: 16 node-version: 24
- name: Checkout
uses: actions/checkout@v4
- name: Execute the scripts - name: Execute the scripts
run: | run: |

View file

@ -7,17 +7,19 @@ on:
jobs: jobs:
releases-matrix: releases-matrix:
name: Release Go binaries name: Release Go binaries
# use oldest available ubuntu to be compatible with more libc.so revs.
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
permissions: permissions:
contents: write contents: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version: '^1.23.6' go-version: '^1.24.9'
check-latest: true
- name: Build - name: Build
run: make dist run: make dist

View file

@ -12,15 +12,15 @@ SHELL = /bin/bash
BUILD = go build BUILD = go build
MKDIR = mkdir -p MKDIR = mkdir -p
.PHONY: build build_linux build_win build_mac_amd64 build_mac_arm64 tag_checked_out mostlyclean .PHONY: build build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64 tag_checked_out mostlyclean
all: all:
@echo choose a target from: build build_linux build_win build_mac_amd64 build_mac_arm64 mostlyclean @echo choose a target from: build build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64 mostlyclean
@echo prepend \`make BUILDTAG=1\` to checkout the highest git tag before building @echo prepend \`make BUILDTAG=1\` to checkout the highest git tag before building
@echo or set BUILDTAG to a specific tag @echo or set BUILDTAG to a specific tag
# Build all binaries # Build all binaries
build: build_linux build_win build_mac_amd64 build_mac_arm64 build: build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64
# if BUILDTAG == 1 set it to the highest git tag # if BUILDTAG == 1 set it to the highest git tag
ifeq ($(strip $(BUILDTAG)),1) ifeq ($(strip $(BUILDTAG)),1)
@ -29,7 +29,7 @@ endif
ifdef BUILDTAG ifdef BUILDTAG
# add the git tag checkout to the requirements of our build targets # add the git tag checkout to the requirements of our build targets
build_linux build_win build_mac_amd64 build_mac_arm64: tag_checked_out build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64: tag_checked_out
endif endif
tag_checked_out: tag_checked_out:
@ -47,13 +47,18 @@ tag_checked_out:
# In this case we might in some situations see an error like # In this case we might in some situations see an error like
# `/bin/bash: line 1: 2b55bbb: value too great for base (error token is "2b55bbb")` # `/bin/bash: line 1: 2b55bbb: value too great for base (error token is "2b55bbb")`
# which can be ignored. # which can be ignored.
GITDESC := $(shell git describe --tags --always) GITDESC := $(shell git describe --tags --always --dirty=-modified 2>/dev/null || true)
CURRENT_FOLDER_NAME := $(notdir $(CURDIR))
ifeq ($(strip $(GITDESC)),)
SEMVER := $(CURRENT_FOLDER_NAME)
else
GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/v?[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/') GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/v?[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/')
SEMVERPATCH := $(shell echo $$(( $(GITDESCPATCH) + 1 ))) SEMVERPATCH := $(shell echo $$(( $(GITDESCPATCH) + 1 )))
# Hint: The second regexp in the next line only matches # Hint: The second regexp in the next line only matches
# if there is a hyphen (`-`) followed by a number, # if there is a hyphen (`-`) followed by a number,
# by which we assume that git describe has added a string after the tag # by which we assume that git describe has added a string after the tag
SEMVER := $(shell echo '$(GITDESC)' | sed -E -e 's/^v//' -e 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' ) SEMVER := $(shell echo '$(GITDESC)' | sed -E -e 's/^v//' -e 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' )
endif
testsemver: testsemver:
@echo from \'$(GITDESC)\' transformed to \'$(SEMVER)\' @echo from \'$(GITDESC)\' transformed to \'$(SEMVER)\'
@ -64,31 +69,49 @@ LDFLAGS = -ldflags "-X github.com/gocsaf/csaf/v3/util.SemVersion=$(SEMVER)"
# Build binaries and place them under bin-$(GOOS)-$(GOARCH) # Build binaries and place them under bin-$(GOOS)-$(GOARCH)
# Using 'Target-specific Variable Values' to specify the build target system # Using 'Target-specific Variable Values' to specify the build target system
GOARCH = amd64 build_linux: GOOS=linux
build_linux: GOOS = linux build_linux: GOARCH=amd64
build_win: GOOS = windows
build_mac_amd64: GOOS = darwin
build_mac_arm64: GOARCH = arm64 build_win: GOOS=windows
build_mac_arm64: GOOS = darwin build_win: GOARCH=amd64
build_linux build_win build_mac_amd64 build_mac_arm64: build_mac_amd64: GOOS=darwin
build_mac_amd64: GOARCH=amd64
build_mac_arm64: GOOS=darwin
build_mac_arm64: GOARCH=arm64
build_linux_arm64: GOOS=linux
build_linux_arm64: GOARCH=arm64
build_win_arm64: GOOS=windows
build_win_arm64: GOARCH=arm64
build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64:
$(eval BINDIR = bin-$(GOOS)-$(GOARCH)/ ) $(eval BINDIR = bin-$(GOOS)-$(GOARCH)/ )
$(MKDIR) $(BINDIR) $(MKDIR) $(BINDIR)
env GOARCH=$(GOARCH) GOOS=$(GOOS) $(BUILD) -o $(BINDIR) $(LDFLAGS) -v ./cmd/... env GOARCH=$(GOARCH) GOOS=$(GOOS) $(BUILD) -o $(BINDIR) $(LDFLAGS) -v ./cmd/...
DISTDIR := csaf-$(SEMVER) DISTDIR := csaf-$(SEMVER)
dist: build_linux build_win build_mac_amd64 build_mac_arm64 dist: build_linux build_linux_arm64 build_win build_win_arm64 build_mac_amd64 build_mac_arm64
mkdir -p dist mkdir -p dist
mkdir -p dist/$(DISTDIR)-windows-amd64/bin-windows-amd64 mkdir -p dist/$(DISTDIR)-windows-amd64/bin-windows-amd64
mkdir -p dist/$(DISTDIR)-windows-arm64/bin-windows-arm64
cp README.md dist/$(DISTDIR)-windows-amd64 cp README.md dist/$(DISTDIR)-windows-amd64
cp README.md dist/$(DISTDIR)-windows-arm64
cp bin-windows-amd64/csaf_uploader.exe bin-windows-amd64/csaf_validator.exe \ cp bin-windows-amd64/csaf_uploader.exe bin-windows-amd64/csaf_validator.exe \
bin-windows-amd64/csaf_checker.exe bin-windows-amd64/csaf_downloader.exe \ bin-windows-amd64/csaf_checker.exe bin-windows-amd64/csaf_downloader.exe \
dist/$(DISTDIR)-windows-amd64/bin-windows-amd64/ dist/$(DISTDIR)-windows-amd64/bin-windows-amd64/
cp bin-windows-arm64/csaf_uploader.exe bin-windows-arm64/csaf_validator.exe \
bin-windows-arm64/csaf_checker.exe bin-windows-arm64/csaf_downloader.exe \
dist/$(DISTDIR)-windows-arm64/bin-windows-arm64/
mkdir -p dist/$(DISTDIR)-windows-amd64/docs mkdir -p dist/$(DISTDIR)-windows-amd64/docs
mkdir -p dist/$(DISTDIR)-windows-arm64/docs
cp docs/csaf_uploader.md docs/csaf_validator.md docs/csaf_checker.md \ cp docs/csaf_uploader.md docs/csaf_validator.md docs/csaf_checker.md \
docs/csaf_downloader.md dist/$(DISTDIR)-windows-amd64/docs docs/csaf_downloader.md dist/$(DISTDIR)-windows-amd64/docs
cp docs/csaf_uploader.md docs/csaf_validator.md docs/csaf_checker.md \
docs/csaf_downloader.md dist/$(DISTDIR)-windows-arm64/docs
mkdir -p dist/$(DISTDIR)-macos/bin-darwin-amd64 \ mkdir -p dist/$(DISTDIR)-macos/bin-darwin-amd64 \
dist/$(DISTDIR)-macos/bin-darwin-arm64 \ dist/$(DISTDIR)-macos/bin-darwin-arm64 \
dist/$(DISTDIR)-macos/docs dist/$(DISTDIR)-macos/docs
@ -98,9 +121,20 @@ dist: build_linux build_win build_mac_amd64 build_mac_arm64
cp docs/$${f}.md dist/$(DISTDIR)-macos/docs ; \ cp docs/$${f}.md dist/$(DISTDIR)-macos/docs ; \
done done
mkdir dist/$(DISTDIR)-gnulinux-amd64 mkdir dist/$(DISTDIR)-gnulinux-amd64
cp -r README.md docs bin-linux-amd64 dist/$(DISTDIR)-gnulinux-amd64 mkdir dist/$(DISTDIR)-gnulinux-arm64
cp -r README.md bin-linux-amd64 dist/$(DISTDIR)-gnulinux-amd64
cp -r README.md bin-linux-arm64 dist/$(DISTDIR)-gnulinux-arm64
# adjust which docs to copy
mkdir -p dist/tmp_docs
cp -r docs/examples dist/tmp_docs
cp docs/*.md dist/tmp_docs
cp -r dist/tmp_docs dist/$(DISTDIR)-gnulinux-amd64/docs
cp -r dist/tmp_docs dist/$(DISTDIR)-gnulinux-arm64/docs
rm -rf dist/tmp_docs
cd dist/ ; zip -r $(DISTDIR)-windows-amd64.zip $(DISTDIR)-windows-amd64/ cd dist/ ; zip -r $(DISTDIR)-windows-amd64.zip $(DISTDIR)-windows-amd64/
cd dist/ ; zip -r $(DISTDIR)-windows-arm64.zip $(DISTDIR)-windows-arm64/
cd dist/ ; tar -cvmlzf $(DISTDIR)-gnulinux-amd64.tar.gz $(DISTDIR)-gnulinux-amd64/ cd dist/ ; tar -cvmlzf $(DISTDIR)-gnulinux-amd64.tar.gz $(DISTDIR)-gnulinux-amd64/
cd dist/ ; tar -cvmlzf $(DISTDIR)-gnulinux-arm64.tar.gz $(DISTDIR)-gnulinux-arm64/
cd dist/ ; tar -cvmlzf $(DISTDIR)-macos.tar.gz $(DISTDIR)-macos cd dist/ ; tar -cvmlzf $(DISTDIR)-macos.tar.gz $(DISTDIR)-macos
# Remove bin-*-* and dist directories # Remove bin-*-* and dist directories

View file

@ -9,14 +9,6 @@
--> -->
> [!IMPORTANT]
> To avoid future breakage, if you still use `csaf-poc`:
> 1. Adjust your HTML links.
> 2. Adjust your go module paths, see [#579](https://github.com/gocsaf/csaf/issues/579#issuecomment-2497244379).
>
> (This repository was moved here on 2024-10-28. The old one is deprecated
> and redirection will be switched off a few months later.)
# csaf # csaf
@ -49,13 +41,20 @@ is a tool for testing a CSAF Trusted Provider according to [Section 7 of the CSA
### [csaf_aggregator](docs/csaf_aggregator.md) ### [csaf_aggregator](docs/csaf_aggregator.md)
is a CSAF Aggregator, to list or mirror providers. is a CSAF Aggregator, to list or mirror providers.
## Other stuff
## Use as go library
The modules of this repository can be used as library by other Go applications. [ISDuBA](https://github.com/ISDuBA/ISDuBA) does so, for example.
But there is only limited support and thus it is _not officially supported_.
There are plans to change this without a concrete schedule within a future major release, e.g. see [#367](https://github.com/gocsaf/csaf/issues/367).
Initially envisioned as a toolbox, it was not constructed as a library,
and to name one issue, exposes too many functions.
This leads to problems like [#634](https://github.com/gocsaf/csaf/issues/634), where we have to accept that with 3.2.0 there was an unintended API change.
### [examples](./examples/README.md) ### [examples](./examples/README.md)
are small examples of how to use `github.com/gocsaf/csaf` are small examples of how to use `github.com/gocsaf/csaf` as an API. Currently this is a work in progress.
as an API. Currently this is a work in progress, as usage of this repository
as a library to access is _not officially supported_, e.g.
see https://github.com/gocsaf/csaf/issues/367 .
## Setup ## Setup
Binaries for the server side are only available and tested Binaries for the server side are only available and tested
@ -79,7 +78,8 @@ Download the binaries from the most recent release assets on Github.
### Build from sources ### Build from sources
- A recent version of **Go** (1.23+) should be installed. [Go installation](https://go.dev/doc/install) - Needs a [supported version](docs/Development.md) of **Go** to be installed.
[Go installation](https://go.dev/doc/install)
- Clone the repository `git clone https://github.com/gocsaf/csaf.git ` - Clone the repository `git clone https://github.com/gocsaf/csaf.git `
@ -107,6 +107,14 @@ Binaries will be placed in directories named like `bin-linux-amd64/` and `bin-wi
For further details of the development process consult our [development page](./docs/Development.md). For further details of the development process consult our [development page](./docs/Development.md).
## Previous repo URLs
> [!NOTE]
> To avoid future breakage, if you have `csaf-poc` in some of your URLs:
> 1. Adjust your HTML links.
> 2. Adjust your go module paths, see [#579](https://github.com/gocsaf/csaf/issues/579#issuecomment-2497244379).
>
> (This repository was moved here from https://github.com/csaf-poc/csaf_distribution on 2024-10-28. The old one is deprecated and redirection will be switched off sometime in 2025.)
## License ## License

View file

@ -13,7 +13,6 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/csv" "encoding/csv"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -25,6 +24,7 @@ import (
"time" "time"
"github.com/gocsaf/csaf/v3/csaf" "github.com/gocsaf/csaf/v3/csaf"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -81,7 +81,7 @@ func (w *worker) checkInterims(
if err := func() error { if err := func() error {
defer res.Body.Close() defer res.Body.Close()
tee := io.TeeReader(res.Body, hasher) tee := io.TeeReader(res.Body, hasher)
return json.NewDecoder(tee).Decode(&doc) return misc.StrictJSONParse(tee, &doc)
}(); err != nil { }(); err != nil {
return nil, err return nil, err
} }

View file

@ -13,7 +13,6 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"io" "io"
"log/slog" "log/slog"
@ -31,6 +30,7 @@ import (
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/gocsaf/csaf/v3/csaf" "github.com/gocsaf/csaf/v3/csaf"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -67,7 +67,7 @@ func (w *worker) mirrorInternal() (*csaf.AggregatorCSAFProvider, error) {
// Collecting the categories per label. // Collecting the categories per label.
w.categories = map[string]util.Set[string]{} w.categories = map[string]util.Set[string]{}
base, err := url.Parse(w.loc) pmdURL, err := url.Parse(w.loc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -76,7 +76,7 @@ func (w *worker) mirrorInternal() (*csaf.AggregatorCSAFProvider, error) {
w.client, w.client,
w.expr, w.expr,
w.metadataProvider, w.metadataProvider,
base) pmdURL)
afp.AgeAccept = w.provider.ageAccept(w.processor.cfg) afp.AgeAccept = w.provider.ageAccept(w.processor.cfg)
@ -538,7 +538,7 @@ func (w *worker) mirrorFiles(tlpLabel csaf.TLPLabel, files []csaf.AdvisoryFile)
download := func(r io.Reader) error { download := func(r io.Reader) error {
tee := io.TeeReader(r, hasher) tee := io.TeeReader(r, hasher)
return json.NewDecoder(tee).Decode(&advisory) return misc.StrictJSONParse(tee, &advisory)
} }
if err := downloadJSON(w.client, file.URL(), download); err != nil { if err := downloadJSON(w.client, file.URL(), download); err != nil {
@ -627,7 +627,6 @@ func (w *worker) mirrorFiles(tlpLabel csaf.TLPLabel, files []csaf.AdvisoryFile)
// If this fails it creates a signature itself with the configured key. // If this fails it creates a signature itself with the configured key.
func (w *worker) downloadSignatureOrSign(url, fname string, data []byte) error { func (w *worker) downloadSignatureOrSign(url, fname string, data []byte) error {
sig, err := w.downloadSignature(url) sig, err := w.downloadSignature(url)
if err != nil { if err != nil {
if err != errNotFound { if err != errNotFound {
w.log.Error("Could not find signature URL", "url", url, "err", err) w.log.Error("Could not find signature URL", "url", url, "err", err)

View file

@ -13,6 +13,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
@ -93,7 +95,12 @@ func (pgs pages) listed(
return err return err
} }
// Links may be relative // Links may be relative
abs := baseURL.ResolveReference(u).String() var abs string
if u.IsAbs() {
abs = u.String()
} else {
abs = misc.JoinURL(baseURL, u).String()
}
content.links.Add(abs) content.links.Add(abs)
return nil return nil
}) })

View file

@ -15,7 +15,6 @@ import (
"crypto/sha512" "crypto/sha512"
"crypto/tls" "crypto/tls"
"encoding/csv" "encoding/csv"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -29,6 +28,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
"golang.org/x/time/rate" "golang.org/x/time/rate"
@ -251,14 +252,14 @@ func (p *processor) run(domains []string) (*Report, error) {
p.reset() p.reset()
if !p.checkProviderMetadata(d) { if !p.checkProviderMetadata(d) {
// We cannot build a report if the provider metadata cannot be parsed. // We need to fail the domain if the PMD cannot be parsed.
log.Printf("Could not parse the Provider-Metadata.json of: %s\n", d) p.badProviderMetadata.use()
continue p.badProviderMetadata.error("Could not parse the Provider-Metadata.json of: %s", d)
} }
if err := p.checkDomain(d); err != nil { if err := p.checkDomain(d); err != nil {
log.Printf("Failed to find valid provider-metadata.json for domain %s: %v. "+ p.badProviderMetadata.use()
"Continuing with next domain.", d, err) p.badProviderMetadata.error("Failed to find valid provider-metadata.json for domain %s: %v. ", d, err)
continue
} }
domain := &Domain{Name: d} domain := &Domain{Name: d}
@ -269,8 +270,10 @@ func (p *processor) run(domains []string) (*Report, error) {
} }
if domain.Role == nil { if domain.Role == nil {
log.Printf("No role found in meta data. Ignoring domain %q\n", d) log.Printf("No role found in meta data for domain %q\n", d)
continue // Assume trusted provider to continue report generation
role := csaf.MetadataRoleTrustedProvider
domain.Role = &role
} }
rules := roleRequirements(*domain.Role) rules := roleRequirements(*domain.Role)
@ -513,7 +516,7 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) {
return nil, nil, fmt.Errorf("%s: %v", feed, err) return nil, nil, fmt.Errorf("%s: %v", feed, err)
} }
var rolieDoc any var rolieDoc any
err = json.NewDecoder(bytes.NewReader(all)).Decode(&rolieDoc) err = misc.StrictJSONParse(bytes.NewReader(all), &rolieDoc)
return rfeed, rolieDoc, err return rfeed, rolieDoc, err
}() }()
if err != nil { if err != nil {
@ -531,7 +534,7 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) {
if len(errors) > 0 { if len(errors) > 0 {
p.badProviderMetadata.error("%s: Validating against JSON schema failed:", feed) p.badProviderMetadata.error("%s: Validating against JSON schema failed:", feed)
for _, msg := range errors { for _, msg := range errors {
p.badProviderMetadata.error(strings.ReplaceAll(msg, `%`, `%%`)) p.badProviderMetadata.error("%s", strings.ReplaceAll(msg, `%`, `%%`))
} }
} }
@ -623,15 +626,9 @@ var yearFromURL = regexp.MustCompile(`.*/(\d{4})/[^/]+$`)
// mistakes, from conforming filenames to invalid advisories. // mistakes, from conforming filenames to invalid advisories.
func (p *processor) integrity( func (p *processor) integrity(
files []csaf.AdvisoryFile, files []csaf.AdvisoryFile,
base string,
mask whereType, mask whereType,
lg func(MessageType, string, ...any), lg func(MessageType, string, ...any),
) error { ) error {
b, err := url.Parse(base)
if err != nil {
return err
}
makeAbs := makeAbsolute(b)
client := p.httpClient() client := p.httpClient()
var data bytes.Buffer var data bytes.Buffer
@ -642,9 +639,8 @@ func (p *processor) integrity(
lg(ErrorType, "Bad URL %s: %v", f, err) lg(ErrorType, "Bad URL %s: %v", f, err)
continue continue
} }
fp = makeAbs(fp)
u := b.ResolveReference(fp).String() u := fp.String()
// Should this URL be ignored? // Should this URL be ignored?
if p.cfg.ignoreURL(u) { if p.cfg.ignoreURL(u) {
@ -699,7 +695,7 @@ func (p *processor) integrity(
if err := func() error { if err := func() error {
defer res.Body.Close() defer res.Body.Close()
tee := io.TeeReader(res.Body, hasher) tee := io.TeeReader(res.Body, hasher)
return json.NewDecoder(tee).Decode(&doc) return misc.StrictJSONParse(tee, &doc)
}(); err != nil { }(); err != nil {
lg(ErrorType, "Reading %s failed: %v", u, err) lg(ErrorType, "Reading %s failed: %v", u, err)
continue continue
@ -738,7 +734,7 @@ func (p *processor) integrity(
switch date, fault := p.extractTime(doc, `initial_release_date`, u); { switch date, fault := p.extractTime(doc, `initial_release_date`, u); {
case fault != "": case fault != "":
p.badFolders.error(fault) p.badFolders.error("%s", fault)
case folderYear == nil: case folderYear == nil:
p.badFolders.error("No year folder found in %s", u) p.badFolders.error("No year folder found in %s", u)
case date.UTC().Year() != *folderYear: case date.UTC().Year() != *folderYear:
@ -746,7 +742,7 @@ func (p *processor) integrity(
} }
current, fault := p.extractTime(doc, `current_release_date`, u) current, fault := p.extractTime(doc, `current_release_date`, u)
if fault != "" { if fault != "" {
p.badChanges.error(fault) p.badChanges.error("%s", fault)
} else { } else {
p.timesAdv[f.URL()] = current p.timesAdv[f.URL()] = current
} }
@ -776,8 +772,7 @@ func (p *processor) integrity(
lg(ErrorType, "Bad URL %s: %v", x.url(), err) lg(ErrorType, "Bad URL %s: %v", x.url(), err)
continue continue
} }
hu = makeAbs(hu) hashFile := hu.String()
hashFile := b.ResolveReference(hu).String()
p.checkTLS(hashFile) p.checkTLS(hashFile)
if res, err = client.Get(hashFile); err != nil { if res, err = client.Get(hashFile); err != nil {
@ -817,7 +812,7 @@ func (p *processor) integrity(
msgType = InfoType msgType = InfoType
} }
for _, fetchError := range hashFetchErrors { for _, fetchError := range hashFetchErrors {
p.badIntegrities.add(msgType, fetchError) p.badIntegrities.add(msgType, "%s", fetchError)
} }
// Check signature // Check signature
@ -826,8 +821,7 @@ func (p *processor) integrity(
lg(ErrorType, "Bad URL %s: %v", f.SignURL(), err) lg(ErrorType, "Bad URL %s: %v", f.SignURL(), err)
continue continue
} }
su = makeAbs(su) sigFile := su.String()
sigFile := b.ResolveReference(su).String()
p.checkTLS(sigFile) p.checkTLS(sigFile)
p.badSignatures.use() p.badSignatures.use()
@ -947,12 +941,13 @@ func (p *processor) checkIndex(base string, mask whereType) error {
scanner := bufio.NewScanner(res.Body) scanner := bufio.NewScanner(res.Body)
for line := 1; scanner.Scan(); line++ { for line := 1; scanner.Scan(); line++ {
u := scanner.Text() u := scanner.Text()
if _, err := url.Parse(u); err != nil { up, err := url.Parse(u)
if err != nil {
p.badIntegrities.error("index.txt contains invalid URL %q in line %d", u, line) p.badIntegrities.error("index.txt contains invalid URL %q in line %d", u, line)
continue continue
} }
files = append(files, csaf.DirectoryAdvisoryFile{Path: u}) files = append(files, csaf.DirectoryAdvisoryFile{Path: misc.JoinURL(bu, up).String()})
} }
return files, scanner.Err() return files, scanner.Err()
}() }()
@ -967,7 +962,7 @@ func (p *processor) checkIndex(base string, mask whereType) error {
// Block rolie checks. // Block rolie checks.
p.labelChecker.feedLabel = "" p.labelChecker.feedLabel = ""
return p.integrity(files, base, mask, p.badIndices.add) return p.integrity(files, mask, p.badIndices.add)
} }
// checkChanges fetches the "changes.csv" and calls the "checkTLS" method for HTTPs checks. // checkChanges fetches the "changes.csv" and calls the "checkTLS" method for HTTPs checks.
@ -1034,9 +1029,13 @@ func (p *processor) checkChanges(base string, mask whereType) error {
} }
path := r[pathColumn] path := r[pathColumn]
times, files = pathURL, err := url.Parse(path)
append(times, t), if err != nil {
append(files, csaf.DirectoryAdvisoryFile{Path: path}) return nil, nil, err
}
times, files = append(times, t),
append(files, csaf.DirectoryAdvisoryFile{Path: misc.JoinURL(bu, pathURL).String()})
p.timesChanges[path] = t p.timesChanges[path] = t
} }
return times, files, nil return times, files, nil
@ -1051,7 +1050,7 @@ func (p *processor) checkChanges(base string, mask whereType) error {
if p.cfg.Range != nil { if p.cfg.Range != nil {
filtered = " (maybe filtered out by time interval)" filtered = " (maybe filtered out by time interval)"
} }
p.badChanges.warn("no entries in changes.csv found" + filtered) p.badChanges.warn("%s", "no entries in changes.csv found"+filtered)
} }
if !sort.SliceIsSorted(times, func(i, j int) bool { if !sort.SliceIsSorted(times, func(i, j int) bool {
@ -1063,7 +1062,7 @@ func (p *processor) checkChanges(base string, mask whereType) error {
// Block rolie checks. // Block rolie checks.
p.labelChecker.feedLabel = "" p.labelChecker.feedLabel = ""
return p.integrity(files, base, mask, p.badChanges.add) return p.integrity(files, mask, p.badChanges.add)
} }
// empty checks if list of strings contains at least one none empty string. // empty checks if list of strings contains at least one none empty string.
@ -1299,7 +1298,7 @@ func (p *processor) checkProviderMetadata(domain string) bool {
for i := range lpmd.Messages { for i := range lpmd.Messages {
p.badProviderMetadata.warn( p.badProviderMetadata.warn(
"Unexpected situation while loading provider-metadata.json: " + "Unexpected situation while loading provider-metadata.json: %s",
lpmd.Messages[i].Message) lpmd.Messages[i].Message)
} }
@ -1364,17 +1363,11 @@ func (p *processor) checkSecurityFolder(folder string) string {
} }
// Try to load // Try to load
up, err := url.Parse(u) _, err = url.Parse(u)
if err != nil { if err != nil {
return fmt.Sprintf("CSAF URL '%s' invalid: %v", u, err) return fmt.Sprintf("CSAF URL '%s' invalid: %v", u, err)
} }
base, err := url.Parse(folder)
if err != nil {
return err.Error()
}
u = base.ResolveReference(up).String()
p.checkTLS(u) p.checkTLS(u)
if res, err = client.Get(u); err != nil { if res, err = client.Get(u); err != nil {
return fmt.Sprintf("Cannot fetch %s from security.txt: %v", u, err) return fmt.Sprintf("Cannot fetch %s from security.txt: %v", u, err)
@ -1406,32 +1399,31 @@ func (p *processor) checkDNS(domain string) {
res, err := client.Get(path) res, err := client.Get(path)
if err != nil { if err != nil {
p.badDNSPath.add(ErrorType, p.badDNSPath.add(ErrorType,
fmt.Sprintf("Fetching %s failed: %v", path, err)) "Fetching %s failed: %v", path, err)
return return
} }
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
p.badDNSPath.add(ErrorType, fmt.Sprintf("Fetching %s failed. Status code %d (%s)", p.badDNSPath.add(ErrorType, "Fetching %s failed. Status code %d (%s)",
path, res.StatusCode, res.Status)) path, res.StatusCode, res.Status)
} }
hash := sha256.New() hash := sha256.New()
defer res.Body.Close() defer res.Body.Close()
content, err := io.ReadAll(res.Body) content, err := io.ReadAll(res.Body)
if err != nil { if err != nil {
p.badDNSPath.add(ErrorType, p.badDNSPath.add(ErrorType,
fmt.Sprintf("Error while reading the response from %s", path)) "Error while reading the response from %s", path)
} }
hash.Write(content) hash.Write(content)
if !bytes.Equal(hash.Sum(nil), p.pmd256) { if !bytes.Equal(hash.Sum(nil), p.pmd256) {
p.badDNSPath.add(ErrorType, p.badDNSPath.add(ErrorType,
fmt.Sprintf("%s does not serve the same provider-metadata.json as previously found", "%s does not serve the same provider-metadata.json as previously found",
path)) path)
} }
} }
// checkWellknown checks if the provider-metadata.json file is // checkWellknown checks if the provider-metadata.json file is
// available under the /.well-known/csaf/ directory. // available under the /.well-known/csaf/ directory.
func (p *processor) checkWellknown(domain string) { func (p *processor) checkWellknown(domain string) {
p.badWellknownMetadata.use() p.badWellknownMetadata.use()
client := p.httpClient() client := p.httpClient()
path := "https://" + domain + "/.well-known/csaf/provider-metadata.json" path := "https://" + domain + "/.well-known/csaf/provider-metadata.json"
@ -1439,11 +1431,12 @@ func (p *processor) checkWellknown(domain string) {
res, err := client.Get(path) res, err := client.Get(path)
if err != nil { if err != nil {
p.badWellknownMetadata.add(ErrorType, p.badWellknownMetadata.add(ErrorType,
fmt.Sprintf("Fetching %s failed: %v", path, err)) "Fetching %s failed: %v", path, err)
return
} }
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
p.badWellknownMetadata.add(ErrorType, fmt.Sprintf("Fetching %s failed. Status code %d (%s)", p.badWellknownMetadata.add(ErrorType, "Fetching %s failed. Status code %d (%s)",
path, res.StatusCode, res.Status)) path, res.StatusCode, res.Status)
} }
} }
@ -1480,13 +1473,13 @@ func (p *processor) checkWellknownSecurityDNS(domain string) error {
// but found in the legacy location, and inform about finding it there (2). // but found in the legacy location, and inform about finding it there (2).
switch warnings { switch warnings {
case 0: case 0:
p.badSecurity.add(InfoType, sDMessage) p.badSecurity.add(InfoType, "%s", sDMessage)
case 1: case 1:
p.badSecurity.add(ErrorType, sDMessage) p.badSecurity.add(ErrorType, "%s", sDMessage)
p.badSecurity.add(ErrorType, sLMessage) p.badSecurity.add(ErrorType, "%s", sLMessage)
case 2: case 2:
p.badSecurity.add(WarnType, sDMessage) p.badSecurity.add(WarnType, "%s", sDMessage)
p.badSecurity.add(InfoType, sLMessage) p.badSecurity.add(InfoType, "%s", sLMessage)
} }
p.checkDNS(domain) p.checkDNS(domain)
@ -1522,11 +1515,6 @@ func (p *processor) checkPGPKeys(_ string) error {
client := p.httpClient() client := p.httpClient()
base, err := url.Parse(p.pmdURL)
if err != nil {
return err
}
for i := range keys { for i := range keys {
key := &keys[i] key := &keys[i]
if key.URL == nil { if key.URL == nil {
@ -1539,10 +1527,11 @@ func (p *processor) checkPGPKeys(_ string) error {
continue continue
} }
u := base.ResolveReference(up).String() // Todo: refactor all methods to directly accept *url.URL
u := up.String()
p.checkTLS(u) p.checkTLS(u)
res, err := client.Get(u) res, err := client.Get(*key.URL)
if err != nil { if err != nil {
p.badPGPs.error("Fetching public OpenPGP key %s failed: %v.", u, err) p.badPGPs.error("Fetching public OpenPGP key %s failed: %v.", u, err)
continue continue

View file

@ -216,11 +216,6 @@ func defaults[T any](p *T, def T) T {
// processROLIEFeeds goes through all ROLIE feeds and checks their // processROLIEFeeds goes through all ROLIE feeds and checks their
// integrity and completeness. // integrity and completeness.
func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error { func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error {
base, err := url.Parse(p.pmdURL)
if err != nil {
return err
}
p.badROLIEFeed.use() p.badROLIEFeed.use()
advisories := map[*csaf.Feed][]csaf.AdvisoryFile{} advisories := map[*csaf.Feed][]csaf.AdvisoryFile{}
@ -232,12 +227,11 @@ func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error {
if feed.URL == nil { if feed.URL == nil {
continue continue
} }
up, err := url.Parse(string(*feed.URL)) feedBase, err := url.Parse(string(*feed.URL))
if err != nil { if err != nil {
p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err) p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err)
continue continue
} }
feedBase := base.ResolveReference(up)
feedURL := feedBase.String() feedURL := feedBase.String()
p.checkTLS(feedURL) p.checkTLS(feedURL)
@ -264,13 +258,12 @@ func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error {
continue continue
} }
up, err := url.Parse(string(*feed.URL)) feedURL, err := url.Parse(string(*feed.URL))
if err != nil { if err != nil {
p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err) p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err)
continue continue
} }
feedURL := base.ResolveReference(up)
feedBase, err := util.BaseURL(feedURL) feedBase, err := util.BaseURL(feedURL)
if err != nil { if err != nil {
p.badProviderMetadata.error("Bad base path: %v", err) p.badProviderMetadata.error("Bad base path: %v", err)
@ -290,7 +283,7 @@ func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error {
// TODO: Issue a warning if we want check AMBER+ without an // TODO: Issue a warning if we want check AMBER+ without an
// authorizing client. // authorizing client.
if err := p.integrity(files, feedBase, rolieMask, p.badProviderMetadata.add); err != nil { if err := p.integrity(files, rolieMask, p.badProviderMetadata.add); err != nil {
if err != errContinue { if err != errContinue {
return err return err
} }
@ -319,13 +312,12 @@ func (p *processor) processROLIEFeeds(feeds [][]csaf.Feed) error {
continue continue
} }
up, err := url.Parse(string(*feed.URL)) feedBase, err := url.Parse(string(*feed.URL))
if err != nil { if err != nil {
p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err) p.badProviderMetadata.error("Invalid URL %s in feed: %v.", *feed.URL, err)
continue continue
} }
feedBase := base.ResolveReference(up)
makeAbs := makeAbsolute(feedBase) makeAbs := makeAbsolute(feedBase)
label := defaults(feed.TLPLabel, csaf.TLPLabelUnlabeled) label := defaults(feed.TLPLabel, csaf.TLPLabelUnlabeled)

View file

@ -35,6 +35,7 @@ import (
"golang.org/x/time/rate" "golang.org/x/time/rate"
"github.com/gocsaf/csaf/v3/csaf" "github.com/gocsaf/csaf/v3/csaf"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -225,7 +226,7 @@ func (d *downloader) download(ctx context.Context, domain string) error {
} }
} }
base, err := url.Parse(lpmd.URL) pmdURL, err := url.Parse(lpmd.URL)
if err != nil { if err != nil {
return fmt.Errorf("invalid URL '%s': %v", lpmd.URL, err) return fmt.Errorf("invalid URL '%s': %v", lpmd.URL, err)
} }
@ -235,7 +236,6 @@ func (d *downloader) download(ctx context.Context, domain string) error {
if err := d.loadOpenPGPKeys( if err := d.loadOpenPGPKeys(
client, client,
lpmd.Document, lpmd.Document,
base,
expr, expr,
); err != nil { ); err != nil {
return err return err
@ -245,7 +245,7 @@ func (d *downloader) download(ctx context.Context, domain string) error {
client, client,
expr, expr,
lpmd.Document, lpmd.Document,
base) pmdURL)
// Do we need time range based filtering? // Do we need time range based filtering?
if d.cfg.Range != nil { if d.cfg.Range != nil {
@ -310,7 +310,6 @@ allFiles:
func (d *downloader) loadOpenPGPKeys( func (d *downloader) loadOpenPGPKeys(
client util.Client, client util.Client,
doc any, doc any,
base *url.URL,
expr *util.PathEval, expr *util.PathEval,
) error { ) error {
src, err := expr.Eval("$.public_openpgp_keys", doc) src, err := expr.Eval("$.public_openpgp_keys", doc)
@ -335,7 +334,7 @@ func (d *downloader) loadOpenPGPKeys(
if key.URL == nil { if key.URL == nil {
continue continue
} }
up, err := url.Parse(*key.URL) u, err := url.Parse(*key.URL)
if err != nil { if err != nil {
slog.Warn("Invalid URL", slog.Warn("Invalid URL",
"url", *key.URL, "url", *key.URL,
@ -343,9 +342,7 @@ func (d *downloader) loadOpenPGPKeys(
continue continue
} }
u := base.ResolveReference(up).String() res, err := client.Get(u.String())
res, err := client.Get(u)
if err != nil { if err != nil {
slog.Warn( slog.Warn(
"Fetching public OpenPGP key failed", "Fetching public OpenPGP key failed",
@ -550,7 +547,7 @@ func (dc *downloadContext) downloadAdvisory(
tee := io.TeeReader(resp.Body, hasher) tee := io.TeeReader(resp.Body, hasher)
if err := json.NewDecoder(tee).Decode(&doc); err != nil { if err := misc.StrictJSONParse(tee, &doc); err != nil {
dc.stats.downloadFailed++ dc.stats.downloadFailed++
slog.Warn("Downloading failed", slog.Warn("Downloading failed",
"url", file.URL(), "url", file.URL(),

View file

@ -11,6 +11,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"net/url"
"os" "os"
"strings" "strings"
@ -262,6 +263,14 @@ func loadConfig() (*config, error) {
if cfg.CanonicalURLPrefix == "" { if cfg.CanonicalURLPrefix == "" {
cfg.CanonicalURLPrefix = "https://" + os.Getenv("SERVER_NAME") cfg.CanonicalURLPrefix = "https://" + os.Getenv("SERVER_NAME")
} }
// Check if canonical url prefix is invalid
parsedURL, err := url.ParseRequestURI(cfg.CanonicalURLPrefix)
if err != nil {
return nil, err
}
if parsedURL.Scheme != "https" && parsedURL.Scheme != "http" {
return nil, fmt.Errorf("invalid canonical URL: %q", cfg.CanonicalURLPrefix)
}
if cfg.TLPs == nil { if cfg.TLPs == nil {
cfg.TLPs = []tlp{tlpCSAF, tlpWhite, tlpGreen, tlpAmber, tlpRed} cfg.TLPs = []tlp{tlpCSAF, tlpWhite, tlpGreen, tlpAmber, tlpRed}

View file

@ -11,7 +11,6 @@ package main
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -82,8 +81,9 @@ func (p *processor) create() error {
} }
defer resp.Body.Close() defer resp.Body.Close()
var createError error
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
log.Printf("Create failed: %s\n", resp.Status) createError = fmt.Errorf("create failed: %s", resp.Status)
} }
var result struct { var result struct {
@ -91,7 +91,7 @@ func (p *processor) create() error {
Errors []string `json:"errors"` Errors []string `json:"errors"`
} }
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { if err := misc.StrictJSONParse(resp.Body, &result); err != nil {
return err return err
} }
@ -101,7 +101,7 @@ func (p *processor) create() error {
writeStrings("Errors:", result.Errors) writeStrings("Errors:", result.Errors)
return nil return createError
} }
// uploadRequest creates the request for uploading a csaf document by passing the filename. // uploadRequest creates the request for uploading a csaf document by passing the filename.
@ -115,7 +115,7 @@ func (p *processor) uploadRequest(filename string) (*http.Request, error) {
if !p.cfg.NoSchemaCheck { if !p.cfg.NoSchemaCheck {
var doc any var doc any
if err := json.NewDecoder(bytes.NewReader(data)).Decode(&doc); err != nil { if err := misc.StrictJSONParse(bytes.NewReader(data), &doc); err != nil {
return nil, err return nil, err
} }
errs, err := csaf.ValidateCSAF(doc) errs, err := csaf.ValidateCSAF(doc)
@ -239,7 +239,7 @@ func (p *processor) process(filename string) error {
Errors []string `json:"errors"` Errors []string `json:"errors"`
} }
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { if err := misc.StrictJSONParse(resp.Body, &result); err != nil {
return err return err
} }

View file

@ -10,7 +10,6 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -19,6 +18,7 @@ import (
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
"github.com/gocsaf/csaf/v3/csaf" "github.com/gocsaf/csaf/v3/csaf"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -301,7 +301,7 @@ func loadJSONFromFile(fname string) (any, error) {
} }
defer f.Close() defer f.Close()
var doc any var doc any
if err = json.NewDecoder(f).Decode(&doc); err != nil { if err = misc.StrictJSONParse(f, &doc); err != nil {
return nil, err return nil, err
} }
return doc, err return doc, err

View file

@ -19,6 +19,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -95,7 +96,7 @@ type AdvisoryFileProcessor struct {
client util.Client client util.Client
expr *util.PathEval expr *util.PathEval
doc any doc any
base *url.URL pmdURL *url.URL
} }
// NewAdvisoryFileProcessor constructs a filename extractor // NewAdvisoryFileProcessor constructs a filename extractor
@ -104,13 +105,13 @@ func NewAdvisoryFileProcessor(
client util.Client, client util.Client,
expr *util.PathEval, expr *util.PathEval,
doc any, doc any,
base *url.URL, pmdURL *url.URL,
) *AdvisoryFileProcessor { ) *AdvisoryFileProcessor {
return &AdvisoryFileProcessor{ return &AdvisoryFileProcessor{
client: client, client: client,
expr: expr, expr: expr,
doc: doc, doc: doc,
base: base, pmdURL: pmdURL,
} }
} }
@ -179,7 +180,7 @@ func (afp *AdvisoryFileProcessor) Process(
// Not found -> fall back to PMD url // Not found -> fall back to PMD url
if empty(dirURLs) { if empty(dirURLs) {
baseURL, err := util.BaseURL(afp.base) baseURL, err := util.BaseURL(afp.pmdURL)
if err != nil { if err != nil {
return err return err
} }
@ -261,8 +262,13 @@ func (afp *AdvisoryFileProcessor) loadChanges(
continue continue
} }
pathURL, err := url.Parse(path)
if err != nil {
return nil, err
}
files = append(files, files = append(files,
DirectoryAdvisoryFile{Path: base.JoinPath(path).String()}) DirectoryAdvisoryFile{Path: misc.JoinURL(base, pathURL).String()})
} }
return files, nil return files, nil
} }
@ -276,12 +282,11 @@ func (afp *AdvisoryFileProcessor) processROLIE(
if feed.URL == nil { if feed.URL == nil {
continue continue
} }
up, err := url.Parse(string(*feed.URL)) feedURL, err := url.Parse(string(*feed.URL))
if err != nil { if err != nil {
slog.Error("Invalid URL in feed", "feed", *feed.URL, "err", err) slog.Error("Invalid URL in feed", "feed", *feed.URL, "err", err)
continue continue
} }
feedURL := afp.base.ResolveReference(up)
slog.Info("Got feed URL", "feed", feedURL) slog.Info("Got feed URL", "feed", feedURL)
fb, err := util.BaseURL(feedURL) fb, err := util.BaseURL(feedURL)
@ -289,11 +294,6 @@ func (afp *AdvisoryFileProcessor) processROLIE(
slog.Error("Invalid feed base URL", "url", fb, "err", err) slog.Error("Invalid feed base URL", "url", fb, "err", err)
continue continue
} }
feedBaseURL, err := url.Parse(fb)
if err != nil {
slog.Error("Cannot parse feed base URL", "url", fb, "err", err)
continue
}
res, err := afp.client.Get(feedURL.String()) res, err := afp.client.Get(feedURL.String())
if err != nil { if err != nil {
@ -325,7 +325,7 @@ func (afp *AdvisoryFileProcessor) processROLIE(
slog.Error("Invalid URL", "url", u, "err", err) slog.Error("Invalid URL", "url", u, "err", err)
return "" return ""
} }
return feedBaseURL.ResolveReference(p).String() return p.String()
} }
rfeed.Entries(func(entry *Entry) { rfeed.Entries(func(entry *Entry) {

View file

@ -14,6 +14,8 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"github.com/gocsaf/csaf/v3/internal/misc"
) )
// Acknowledgement reflects the 'acknowledgement' object in the list of acknowledgements. // Acknowledgement reflects the 'acknowledgement' object in the list of acknowledgements.
@ -383,7 +385,6 @@ type Relationship struct {
FullProductName *FullProductName `json:"full_product_name"` // required FullProductName *FullProductName `json:"full_product_name"` // required
ProductReference *ProductID `json:"product_reference"` // required ProductReference *ProductID `json:"product_reference"` // required
RelatesToProductReference *ProductID `json:"relates_to_product_reference"` // required RelatesToProductReference *ProductID `json:"relates_to_product_reference"` // required
} }
// Relationships is a list of Relationship. // Relationships is a list of Relationship.
@ -446,6 +447,7 @@ type Flag struct {
Date *string `json:"date,omitempty"` Date *string `json:"date,omitempty"`
GroupIDs *ProductGroups `json:"group_ids,omitempty"` GroupIDs *ProductGroups `json:"group_ids,omitempty"`
Label *FlagLabel `json:"label"` // required Label *FlagLabel `json:"label"` // required
//revive:disable-next-line:var-naming until new major version w fix
ProductIds *Products `json:"product_ids,omitempty"` ProductIds *Products `json:"product_ids,omitempty"`
} }
@ -609,8 +611,10 @@ type Remediation struct {
Date *string `json:"date,omitempty"` Date *string `json:"date,omitempty"`
Details *string `json:"details"` // required Details *string `json:"details"` // required
Entitlements []*string `json:"entitlements,omitempty"` Entitlements []*string `json:"entitlements,omitempty"`
//revive:disable:var-naming until new major version w fix
GroupIds *ProductGroups `json:"group_ids,omitempty"` GroupIds *ProductGroups `json:"group_ids,omitempty"`
ProductIds *Products `json:"product_ids,omitempty"` ProductIds *Products `json:"product_ids,omitempty"`
//revive:enable
RestartRequired *RestartRequired `json:"restart_required,omitempty"` RestartRequired *RestartRequired `json:"restart_required,omitempty"`
URL *string `json:"url,omitempty"` URL *string `json:"url,omitempty"`
} }
@ -741,8 +745,10 @@ type Threat struct {
Category *ThreatCategory `json:"category"` // required Category *ThreatCategory `json:"category"` // required
Date *string `json:"date,omitempty"` Date *string `json:"date,omitempty"`
Details *string `json:"details"` // required Details *string `json:"details"` // required
//revive:disable:var-naming until new major version w fix
GroupIds *ProductGroups `json:"group_ids,omitempty"` GroupIds *ProductGroups `json:"group_ids,omitempty"`
ProductIds *Products `json:"product_ids,omitempty"` ProductIds *Products `json:"product_ids,omitempty"`
//revive:enable
} }
// Threats is a list of Threat elements. // Threats is a list of Threat elements.
@ -885,8 +891,8 @@ func (rs Revisions) Validate() error {
// Validate validates an Engine. // Validate validates an Engine.
func (e *Engine) Validate() error { func (e *Engine) Validate() error {
if e.Version == nil { if e.Name == nil {
return errors.New("'version' is missing") return errors.New("'name' is missing")
} }
return nil return nil
} }
@ -1391,7 +1397,7 @@ func LoadAdvisory(fname string) (*Advisory, error) {
} }
defer f.Close() defer f.Close()
var advisory Advisory var advisory Advisory
if err := json.NewDecoder(f).Decode(&advisory); err != nil { if err := misc.StrictJSONParse(f, &advisory); err != nil {
return nil, err return nil, err
} }
if err := advisory.Validate(); err != nil { if err := advisory.Validate(); err != nil {

46
csaf/advisory_test.go Normal file
View file

@ -0,0 +1,46 @@
package csaf
import (
"os"
"path/filepath"
"testing"
)
func TestLoadAdvisory(t *testing.T) {
type args struct {
jsonDir string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Valid documents",
args: args{jsonDir: "csaf-documents/valid"},
wantErr: false,
},
{
name: "Garbage trailing data",
args: args{jsonDir: "csaf-documents/trailing-garbage-data"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := filepath.Walk("../testdata/"+tt.args.jsonDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Mode().IsRegular() && filepath.Ext(info.Name()) == ".json" {
if _, err := LoadAdvisory(path); (err != nil) != tt.wantErr {
t.Errorf("LoadAdvisory() error = %v, wantErr %v", err, tt.wantErr)
}
}
return nil
}); err != nil {
t.Fatal(err)
}
})
}
}

View file

@ -6,7 +6,11 @@
// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2023 Intevation GmbH <https://intevation.de> // Software-Engineering: 2023 Intevation GmbH <https://intevation.de>
// Package csaf contains the core data models used by the csaf distribution. // Package csaf contains the core data models used by the csaf distribution
// tools.
//
// See https://github.com/gocsaf/csaf/tab=readme-ov-file#use-as-go-library
// about hints and limits for its use as a library.
package csaf package csaf
//go:generate go run ./generate_cvss_enums.go -o cvss20enums.go -i ./schema/cvss-v2.0.json -p CVSS20 //go:generate go run ./generate_cvss_enums.go -o cvss20enums.go -i ./schema/cvss-v2.0.json -p CVSS20

View file

@ -12,7 +12,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"go/format" "go/format"
@ -22,6 +21,8 @@ import (
"sort" "sort"
"strings" "strings"
"text/template" "text/template"
"github.com/gocsaf/csaf/v3/internal/misc"
) )
// We from Intevation consider the source code parts in the following // We from Intevation consider the source code parts in the following
@ -98,7 +99,7 @@ func loadSchema(filename string) (*schema, error) {
} }
defer f.Close() defer f.Close()
var s schema var s schema
if err := json.NewDecoder(f).Decode(&s); err != nil { if err := misc.StrictJSONParse(f, &s); err != nil {
return nil, err return nil, err
} }
return &s, nil return &s, nil

View file

@ -17,6 +17,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -575,7 +576,6 @@ func (d *Distribution) Validate() error {
// Validate checks if the provider metadata is valid. // Validate checks if the provider metadata is valid.
// Returns an error if the validation fails otherwise nil. // Returns an error if the validation fails otherwise nil.
func (pmd *ProviderMetadata) Validate() error { func (pmd *ProviderMetadata) Validate() error {
switch { switch {
case pmd.CanonicalURL == nil: case pmd.CanonicalURL == nil:
return errors.New("canonical_url is mandatory") return errors.New("canonical_url is mandatory")
@ -695,8 +695,7 @@ func (pmd *ProviderMetadata) WriteTo(w io.Writer) (int64, error) {
func LoadProviderMetadata(r io.Reader) (*ProviderMetadata, error) { func LoadProviderMetadata(r io.Reader) (*ProviderMetadata, error) {
var pmd ProviderMetadata var pmd ProviderMetadata
dec := json.NewDecoder(r) if err := misc.StrictJSONParse(r, &pmd); err != nil {
if err := dec.Decode(&pmd); err != nil {
return nil, err return nil, err
} }

View file

@ -11,13 +11,13 @@ package csaf
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/json"
"fmt" "fmt"
"io" "io"
"log/slog" "log/slog"
"net/http" "net/http"
"strings" "strings"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -33,7 +33,7 @@ type ProviderMetadataLoader struct {
type ProviderMetadataLoadMessageType int type ProviderMetadataLoadMessageType int
const ( const (
//JSONDecodingFailed indicates problems with JSON decoding // JSONDecodingFailed indicates problems with JSON decoding
JSONDecodingFailed ProviderMetadataLoadMessageType = iota JSONDecodingFailed ProviderMetadataLoadMessageType = iota
// SchemaValidationFailed indicates a general problem with schema validation. // SchemaValidationFailed indicates a general problem with schema validation.
SchemaValidationFailed SchemaValidationFailed
@ -149,7 +149,6 @@ func (pmdl *ProviderMetadataLoader) Enumerate(domain string) []*LoadedProviderMe
} }
dnsURL := "https://csaf.data.security." + domain dnsURL := "https://csaf.data.security." + domain
return []*LoadedProviderMetadata{pmdl.loadFromURL(dnsURL)} return []*LoadedProviderMetadata{pmdl.loadFromURL(dnsURL)}
} }
// Load loads one valid provider metadata for a given path. // Load loads one valid provider metadata for a given path.
@ -323,7 +322,7 @@ func (pmdl *ProviderMetadataLoader) loadFromURL(path string) *LoadedProviderMeta
var doc any var doc any
if err := json.NewDecoder(tee).Decode(&doc); err != nil { if err := misc.StrictJSONParse(tee, &doc); err != nil {
result.Messages.Add( result.Messages.Add(
JSONDecodingFailed, JSONDecodingFailed,
fmt.Sprintf("JSON decoding failed: %v", err)) fmt.Sprintf("JSON decoding failed: %v", err))

View file

@ -18,6 +18,7 @@ import (
"net/http" "net/http"
"sync" "sync"
"github.com/gocsaf/csaf/v3/internal/misc"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
) )
@ -180,7 +181,6 @@ func prepareCache(config string) (cache, error) {
return create() return create()
} }
return nil return nil
}); err != nil { }); err != nil {
db.Close() db.Close()
return nil, err return nil, err
@ -256,7 +256,7 @@ func deserialize(value []byte) (*RemoteValidationResult, error) {
} }
defer r.Close() defer r.Close()
var rvr RemoteValidationResult var rvr RemoteValidationResult
if err := json.NewDecoder(r).Decode(&rvr); err != nil { if err := misc.StrictJSONParse(r, &rvr); err != nil {
return nil, err return nil, err
} }
return &rvr, nil return &rvr, nil
@ -323,7 +323,7 @@ func (v *remoteValidator) Validate(doc any) (*RemoteValidationResult, error) {
// no cache -> process directly. // no cache -> process directly.
in = resp.Body in = resp.Body
} }
return json.NewDecoder(in).Decode(&rvr) return misc.StrictJSONParse(in, &rvr)
}(); err != nil { }(); err != nil {
return nil, err return nil, err
} }

View file

@ -14,6 +14,7 @@ import (
"sort" "sort"
"time" "time"
"github.com/gocsaf/csaf/v3/internal/misc"
"github.com/gocsaf/csaf/v3/util" "github.com/gocsaf/csaf/v3/util"
) )
@ -54,7 +55,7 @@ type ROLIEServiceDocument struct {
// LoadROLIEServiceDocument loads a ROLIE service document from a reader. // LoadROLIEServiceDocument loads a ROLIE service document from a reader.
func LoadROLIEServiceDocument(r io.Reader) (*ROLIEServiceDocument, error) { func LoadROLIEServiceDocument(r io.Reader) (*ROLIEServiceDocument, error) {
var rsd ROLIEServiceDocument var rsd ROLIEServiceDocument
if err := json.NewDecoder(r).Decode(&rsd); err != nil { if err := misc.StrictJSONParse(r, &rsd); err != nil {
return nil, err return nil, err
} }
return &rsd, nil return &rsd, nil
@ -122,7 +123,7 @@ func (rcd *ROLIECategoryDocument) Merge(categories ...string) bool {
// LoadROLIECategoryDocument loads a ROLIE category document from a reader. // LoadROLIECategoryDocument loads a ROLIE category document from a reader.
func LoadROLIECategoryDocument(r io.Reader) (*ROLIECategoryDocument, error) { func LoadROLIECategoryDocument(r io.Reader) (*ROLIECategoryDocument, error) {
var rcd ROLIECategoryDocument var rcd ROLIECategoryDocument
if err := json.NewDecoder(r).Decode(&rcd); err != nil { if err := misc.StrictJSONParse(r, &rcd); err != nil {
return nil, err return nil, err
} }
return &rcd, nil return &rcd, nil
@ -168,14 +169,22 @@ type Format struct {
// Entry for ROLIE. // Entry for ROLIE.
type Entry struct { type Entry struct {
Base *string `json:"base,omitempty"`
LanguageTag *string `json:"lang,omitempty"`
Author *json.RawMessage `json:"author,omitempty"`
Category []ROLIECategory `json:"category,omitempty"`
Content Content `json:"content"`
Contributor *json.RawMessage `json:"contributor,omitempty"`
ID string `json:"id"` ID string `json:"id"`
Titel string `json:"title"`
Link []Link `json:"link"` Link []Link `json:"link"`
Published TimeStamp `json:"published"` Published TimeStamp `json:"published"`
Updated TimeStamp `json:"updated"` Rights *json.RawMessage `json:"rights,omitempty"`
Source *json.RawMessage `json:"source,omitempty"`
Summary *Summary `json:"summary,omitempty"` Summary *Summary `json:"summary,omitempty"`
Content Content `json:"content"` Titel string `json:"title"`
Updated TimeStamp `json:"updated"`
Format Format `json:"format"` Format Format `json:"format"`
Property *json.RawMessage `json:"property,omitempty"`
} }
// FeedData is the content of the ROLIE feed. // FeedData is the content of the ROLIE feed.
@ -195,9 +204,8 @@ type ROLIEFeed struct {
// LoadROLIEFeed loads a ROLIE feed from a reader. // LoadROLIEFeed loads a ROLIE feed from a reader.
func LoadROLIEFeed(r io.Reader) (*ROLIEFeed, error) { func LoadROLIEFeed(r io.Reader) (*ROLIEFeed, error) {
dec := json.NewDecoder(r)
var rf ROLIEFeed var rf ROLIEFeed
if err := dec.Decode(&rf); err != nil { if err := misc.StrictJSONParse(r, &rf); err != nil {
return nil, err return nil, err
} }
return &rf, nil return &rf, nil

View file

@ -10,13 +10,17 @@ package csaf
import ( import (
"bytes" "bytes"
"crypto/tls"
_ "embed" // Used for embedding. _ "embed" // Used for embedding.
"io" "errors"
"fmt"
"net/http"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"time"
"github.com/santhosh-tekuri/jsonschema/v5" "github.com/santhosh-tekuri/jsonschema/v6"
) )
//go:embed schema/csaf_json_schema.json //go:embed schema/csaf_json_schema.json
@ -64,13 +68,28 @@ var (
compiledRolieSchema = compiledSchema{url: rolieSchemaURL} compiledRolieSchema = compiledSchema{url: rolieSchemaURL}
) )
// loadURL loads the content of an URL from embedded data or type schemaLoader http.Client
// falls back to the global loader function of the jsonschema package.
func loadURL(s string) (io.ReadCloser, error) { func (l *schemaLoader) loadHTTPURL(url string) (any, error) {
loader := func(data []byte) (io.ReadCloser, error) { client := (*http.Client)(l)
return io.NopCloser(bytes.NewReader(data)), nil resp, err := client.Get(url)
if err != nil {
return nil, err
} }
switch s { defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s returned status code %d", url, resp.StatusCode)
}
return jsonschema.UnmarshalJSON(resp.Body)
}
// Load loads the schema from the specified url.
func (l *schemaLoader) Load(url string) (any, error) {
loader := func(data []byte) (any, error) {
return jsonschema.UnmarshalJSON(bytes.NewReader(data))
}
switch url {
case csafSchemaURL: case csafSchemaURL:
return loader(csafSchema) return loader(csafSchema)
case cvss20SchemaURL: case cvss20SchemaURL:
@ -86,14 +105,27 @@ func loadURL(s string) (io.ReadCloser, error) {
case rolieSchemaURL: case rolieSchemaURL:
return loader(rolieSchema) return loader(rolieSchema)
default: default:
return jsonschema.LoadURL(s) // Fallback to http loader
return l.loadHTTPURL(url)
} }
} }
func newSchemaLoader(insecure bool) *schemaLoader {
httpLoader := schemaLoader(http.Client{
Timeout: 15 * time.Second,
})
if insecure {
httpLoader.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
return &httpLoader
}
func (cs *compiledSchema) compile() { func (cs *compiledSchema) compile() {
c := jsonschema.NewCompiler() c := jsonschema.NewCompiler()
c.AssertFormat = true c.AssertFormat()
c.LoadURL = loadURL c.UseLoader(newSchemaLoader(false))
cs.compiled, cs.err = c.Compile(cs.url) cs.compiled, cs.err = c.Compile(cs.url)
} }
@ -109,7 +141,8 @@ func (cs *compiledSchema) validate(doc any) ([]string, error) {
return nil, nil return nil, nil
} }
valErr, ok := err.(*jsonschema.ValidationError) var valErr *jsonschema.ValidationError
ok := errors.As(err, &valErr)
if !ok { if !ok {
return nil, err return nil, err
} }
@ -133,21 +166,21 @@ func (cs *compiledSchema) validate(doc any) ([]string, error) {
if pi != pj { if pi != pj {
return pi < pj return pi < pj
} }
return errs[i].Error < errs[j].Error return errs[i].Error.String() < errs[j].Error.String()
}) })
res := make([]string, 0, len(errs)) res := make([]string, 0, len(errs))
for i := range errs { for i := range errs {
e := &errs[i] e := &errs[i]
if e.Error == "" { if e.Error == nil {
continue continue
} }
loc := e.InstanceLocation loc := e.InstanceLocation
if loc == "" { if loc == "" {
loc = e.AbsoluteKeywordLocation loc = e.AbsoluteKeywordLocation
} }
res = append(res, loc+": "+e.Error) res = append(res, loc+": "+e.Error.String())
} }
return res, nil return res, nil

View file

@ -3,7 +3,7 @@
## Supported Go versions ## Supported Go versions
We support the latest version and the one before We support the latest version and the one before
the latest version of Go (currently 1.23 and 1.24). the latest version of Go (currently 1.24 and 1.25).
## Generated files ## Generated files

View file

@ -247,3 +247,9 @@ insecure = true
In case you want to provide CSAF advisories from others In case you want to provide CSAF advisories from others
that only qualify as CSAF publishers, see that only qualify as CSAF publishers, see
[how to use the `csaf_aggregator` as "CSAF proxy provider"](proxy-provider-for-aggregator.md). [how to use the `csaf_aggregator` as "CSAF proxy provider"](proxy-provider-for-aggregator.md).
Some providers may limit the rate of requests that may be sent to retrieve advisories.
This may cause issues with the aggregator.
In this case, the --rate option can be used to adjust the requests per second
sent by each worker of the aggregator to an acceptable rate.
(The rate that is considered acceptable depends on the provider.)

View file

@ -78,6 +78,13 @@ The option `timerange` allows to only check advisories from a given time
interval. It can only be given once. See the interval. It can only be given once. See the
[downloader documentation](csaf_downloader.md#timerange-option) for details. [downloader documentation](csaf_downloader.md#timerange-option) for details.
Some providers may limit the rate of requests that may be sent to retrieve advisories.
This may cause the checker to be unable to retrieve all advisories. In this case,
the --rate option can be used to adjust the requests per second
sent by the checker to an acceptable rate.
(The rate that is considered acceptable depends on the provider.)
You can ignore certain advisories while checking by specifying a list You can ignore certain advisories while checking by specifying a list
of regular expressions[^1] to match their URLs by using the `ignorepattern` of regular expressions[^1] to match their URLs by using the `ignorepattern`
option. option.

View file

@ -51,6 +51,12 @@ to download more advisories at once. This may improve the overall speed of the d
However, since this also increases the load on the servers, their administrators could However, since this also increases the load on the servers, their administrators could
have taken countermeasures to limit this. have taken countermeasures to limit this.
For example, some providers may limit the rate of requests that may be sent to retrieve advisories.
This may cause the downloader to be unable to retrieve all advisories.
In this case, the --rate option can be used to adjust the requests per second
sent by the downloader to an acceptable rate.
(The rate that is considered acceptable depends on the provider.)
If no config file is explictly given the follwing places are searched for a config file: If no config file is explictly given the follwing places are searched for a config file:
``` ```
@ -69,7 +75,7 @@ insecure = false
# client_cert # not set by default # client_cert # not set by default
# client_key # not set by default # client_key # not set by default
# client_passphrase # not set by default # client_passphrase # not set by default
ignoresigcheck = false ignore_sigcheck = false
# rate # set to unlimited # rate # set to unlimited
worker = 2 worker = 2
# time_range # not set by default # time_range # not set by default
@ -104,8 +110,9 @@ ignorepattern = [".*white.*", ".*red.*"]
#### Timerange option #### Timerange option
The `timerange` parameter enables downloading advisories which last changes falls The `time_range` parameter enables downloading advisories
into a given intervall. There are three possible notations: which last changes falls into a given intervall.
There are three possible notations:
1. Relative. If the given string follows the rules of a 1. Relative. If the given string follows the rules of a
[Go duration](https://pkg.go.dev/time@go1.20.6#ParseDuration), [Go duration](https://pkg.go.dev/time@go1.20.6#ParseDuration),

View file

@ -58,7 +58,8 @@ The following example file documents all available configuration options:
# The following shows an example of a manually set prefix: # The following shows an example of a manually set prefix:
#canonical_url_prefix = "https://localhost" #canonical_url_prefix = "https://localhost"
# Require users to use a password and a valid Client Certificate for write access. # Require users to use both
# (1) a password and (2) a valid Client Certificate for write access.
#certificate_and_password = false #certificate_and_password = false
# Allow the user to send the request without having to send a passphrase # Allow the user to send the request without having to send a passphrase

View file

@ -144,7 +144,7 @@ on a GNU/Linux operating system.
Create the folders: Create the folders:
```(shell) ```(shell)
curl https://192.168.56.102/cgi-bin/csaf_provider.go/create --cert-type p12 --cert {clientCertificat.p12} curl https://192.168.56.102/cgi-bin/csaf_provider.go/api/create --cert-type p12 --cert {clientCertificat.p12}
``` ```
Replace {clientCertificate.p12} with the client certificate file Replace {clientCertificate.p12} with the client certificate file
in pkcs12 format which includes the corresponding key as well. in pkcs12 format which includes the corresponding key as well.

View file

@ -1,5 +1,5 @@
Scripts for assisting the Integration tests. Scripts for assisting the Integration tests.
They were written on Ubuntu 20.04 TLS amd64 and also tested with 24.04 TLS. They were written on Ubuntu 20.04 LTS amd64 and also tested with 24.04 LTS.
- `prepareUbuntuInstanceForITests.sh` installs the required packages for the csaf integration tests on a naked Ubuntu LTS amd64. - `prepareUbuntuInstanceForITests.sh` installs the required packages for the csaf integration tests on a naked Ubuntu LTS amd64.
@ -8,9 +8,9 @@ and configures nginx for serving TLS connections.
- `TLSClientConfigsForITest.sh` generates client certificates by calling `createCCForITest.sh` which uses the root certificate initialized before with `createRootCAForITest.sh`. It configures nginx to enable the authentication with client certificate. (This assumes that the same folder name is used to create the root certificate) - `TLSClientConfigsForITest.sh` generates client certificates by calling `createCCForITest.sh` which uses the root certificate initialized before with `createRootCAForITest.sh`. It configures nginx to enable the authentication with client certificate. (This assumes that the same folder name is used to create the root certificate)
- `setupProviderForITest.sh` builds the csaf_provider, writes the required nginx configurations and create the initial folders. IT calls `uploadToProvider.sh` to upload some csaf example files to the provider. - `setupProviderForITest.sh` builds the `csaf_provider`, writes the required nginx configurations and create the initial folders. IT calls `uploadToProvider.sh` to upload some csaf example files to the provider.
As creating the folders needs to authenticate with the csaf_provider, the configurations of TLS server and Client certificate authentication should be set. So it is recommended to call the scripts in this order: `TLSConfigsForITest.sh`, `TLSClientConfigsForITest.sh`, `setupProviderForITest.sh` As creating the folders needs to authenticate with the `csaf_provider`, the configurations of TLS server and Client certificate authentication should be set. So it is recommended to call the scripts in this order: `TLSConfigsForITest.sh`, `TLSClientConfigsForITest.sh`, `setupProviderForITest.sh`
Calling example (as user with sudo privileges): Calling example (as user with sudo privileges):
``` bash ``` bash

34
go.mod
View file

@ -1,31 +1,33 @@
module github.com/gocsaf/csaf/v3 module github.com/gocsaf/csaf/v3
go 1.22.9 go 1.24.9
toolchain go1.25.3
require ( require (
github.com/BurntSushi/toml v1.4.0 github.com/BurntSushi/toml v1.5.0
github.com/Intevation/gval v1.3.0 github.com/Intevation/gval v1.3.0
github.com/Intevation/jsonpath v0.2.1 github.com/Intevation/jsonpath v0.2.1
github.com/ProtonMail/gopenpgp/v2 v2.8.0 github.com/ProtonMail/gopenpgp/v2 v2.9.0
github.com/PuerkitoBio/goquery v1.8.1 github.com/PuerkitoBio/goquery v1.11.0
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.13.0
github.com/jessevdk/go-flags v1.6.1 github.com/jessevdk/go-flags v1.6.1
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
go.etcd.io/bbolt v1.3.11 go.etcd.io/bbolt v1.4.3
golang.org/x/crypto v0.29.0 golang.org/x/crypto v0.46.0
golang.org/x/term v0.26.0 golang.org/x/term v0.38.0
golang.org/x/time v0.8.0 golang.org/x/time v0.14.0
) )
require ( require (
github.com/ProtonMail/go-crypto v1.1.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/cloudflare/circl v1.5.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
golang.org/x/net v0.31.0 // indirect golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.27.0 // indirect golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.20.0 // indirect golang.org/x/text v0.32.0 // indirect
) )

114
go.sum
View file

@ -1,28 +1,30 @@
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw=
github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o= github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o=
github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A= github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A=
github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw= github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw=
github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.8.0 h1:WvMv3CMcFsqKSM4/Qf8sf3tgyQkzDqQmoSE49bnBuP4= github.com/ProtonMail/gopenpgp/v2 v2.9.0 h1:ruLzBmwe4dR1hdnrsEJ/S7psSBmV15gFttFUPP/+/kE=
github.com/ProtonMail/gopenpgp/v2 v2.8.0/go.mod h1:qb2GUSnmA9ipBW5GVtCtEhkummSlqs2A8Ar3S0HBgSY= github.com/ProtonMail/gopenpgp/v2 v2.9.0/go.mod h1:IldDyh9Hv1ZCCYatTuuEt1XZJ0OPjxLpTarDfglih7s=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@ -31,67 +33,93 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View file

@ -20,13 +20,13 @@ func TestLoadCertificates(t *testing.T) {
passphrase = "qwer" passphrase = "qwer"
missingCert = "data/testclientcert_missing.crt" missingCert = "data/testclientcert_missing.crt"
missingTestkey = "data/testclientkey_missing.pem" missingTestkey = "data/testclientkey_missing.pem"
privateKey = "data/privated.pem" privateKey = "data/private.pem"
privateCert = "data/cert.crt" privateCert = "data/cert.crt"
) )
// Try to load cert that is not protected, expect success. // Try to load cert that is not protected, expect success.
if cert, err := LoadCertificate(&testCert, &testKey, nil); cert == nil || err != nil { if cert, err := LoadCertificate(&testCert, &testKey, nil); cert == nil || err != nil {
t.Errorf("Failure: Couldn't load supposedly valid certificate.") t.Errorf("Failure: Couldn't load supposedly valid certificate. Got error: %v", err)
} }
// Try to load no cert, expect error. // Try to load no cert, expect error.
if cert, err := LoadCertificate(nil, &testKey, nil); cert != nil || err == nil { if cert, err := LoadCertificate(nil, &testKey, nil); cert != nil || err == nil {
@ -46,7 +46,7 @@ func TestLoadCertificates(t *testing.T) {
} }
// Try to load encrypted cert, expecting success. // Try to load encrypted cert, expecting success.
if cert, err := LoadCertificate(&privateCert, &privateKey, &passphrase); cert == nil || err != nil { if cert, err := LoadCertificate(&privateCert, &privateKey, &passphrase); cert == nil || err != nil {
t.Errorf("Failure: Couldn't load supposedly valid encrypted certificate.") t.Errorf("Failure: Couldn't load supposedly valid encrypted certificate. Got error: %v", err)
} }
// Try to load wrong encrypted cert, expecting error. // Try to load wrong encrypted cert, expecting error.
if cert, err := LoadCertificate(&testKey, &privateKey, &passphrase); cert != nil || err == nil { if cert, err := LoadCertificate(&testKey, &privateKey, &passphrase); cert != nil || err == nil {
@ -56,8 +56,8 @@ func TestLoadCertificates(t *testing.T) {
if cert, err := LoadCertificate(&missingCert, &privateKey, &passphrase); cert != nil || err == nil { if cert, err := LoadCertificate(&missingCert, &privateKey, &passphrase); cert != nil || err == nil {
t.Errorf("Failure: No Failure while loading nonexistens certificate.") t.Errorf("Failure: No Failure while loading nonexistens certificate.")
} }
// Try to load nonexistent encrypted cert, expecting error. // Try to load nonexistent encrypted cert, expecting success.
if cert, err := LoadCertificate(nil, nil, nil); cert != nil || err != nil { if cert, err := LoadCertificate(nil, nil, nil); cert != nil || err != nil {
t.Errorf("Failure: Expected nil return.") t.Errorf("Failure: Expected nil return. Got error: %v", err)
} }
} }

View file

@ -0,0 +1,60 @@
#!/bin/bash
#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
# Software-Engineering: 2025 Intevation GmbH <https://intevation.de>
# cab be used to generated the certificates for the go tests
# as the resulting files are in the repository, this script does not
# need to be run each time, its purpose is to document how the keys and
# certs were created
set -e
certtool --generate-privkey --outfile testserver-key.pem
echo '
organization = "CSAF"
unit = "CSAF Distribution"
country = "DE"
cn = "csaf.test"
dns_name = "csaf.test"
dns_name = "localhost"
dns_name = "*.csaf.test"
ip_address = "127.0.0.1"
ip_address = "::1"
tls_www_server
tls_www_client
ocsp_signing_key
encryption_key
signing_key
expiration_days = 36500
' > gnutls-certtool.testserver.template
certtool --generate-self-signed --load-privkey testserver-key.pem --outfile cert.crt --template gnutls-certtool.testserver.template --stdout | head -1
# for testing legacy code path, we use openssl's traditional mode to
# create a password protected variant after RFC 1423 that still can be read
# by https://pkg.go.dev/crypto/x509#DecryptPEMBlock. Citation:
# Legacy PEM encryption as specified in RFC 1423 is insecure by design.
# Since it does not authenticate the ciphertext, it is vulnerable
# to padding oracle attacks that can let an attacker recover the plaintext.
openssl rsa -in testserver-key.pem -out private.pem -aes256 -passout pass:qwer -traditional
echo '
organization = "CSAF Tools Development (internal)"
country = "DE"
cn = "Tester"
tls_www_client
encryption_key
signing_key
expiration_days = 36500
' > gnutls-certtool.testclientkey.template
certtool --generate-privkey --bits 3072 --outfile testclientkey.pem
certtool --generate-self-signed --load-privkey testclientkey.pem --template gnutls-certtool.testclientkey.template --outfile testclient.crt

View file

@ -1,37 +1,28 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
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
t4IrYSBkbnNOYW1lIG9mIHRoZSBzdWJqZWN0IG9mIHRoZSBjZXJ0aWZpY2F0ZYI3 c9SoHpWzh1ZuzfiBYa/cdQNUtaTfgHLi5GYtV1DzmKXVRUciBiNBWWxYMbTGvTOO
YW4gYWRkaXRpb25hbCBkbnNOYW1lIG9mIHRoZSBzdWJqZWN0IG9mIHRoZSBjZXJ0 i3r6DEgOYuukeL4qj//EGOcTJEarHVSxPMuXTD/PoP/VpIdqRS9drEpFUC6lecZc
aWZpY2F0ZYIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIBLoIP UJtUPAcyx0oD2vNmPmulDfYFMLLOPrIeNa0g7os4wgUl7+9wR1cPPRTXY0fW6Hoi
c2Vjb25kIGFkZGl0aW9ugg50aGlyZCBhZGRpdGlvboIHZG5zTmFtZTAMBgNVHRMB j+a8Qn80Q3PrOuEO/SZ4aHHpOk90bRqofyIhFjPwS0YN5w/Sn23uq1u2Dx+Zy+5K
Af8EAjAAMDEGA1UdJQQqMCgGCCsGAQUFBwMJBggrBgEFBQcDAgYIKwYBBQUHAwEG 6Cs9p5dJWu5/zU4ZdbQlpYIHXQVbido1TY92Z84skEsac2wVh7L2LMB3p3Gu9WYn
CCsGAQUFBwMCMA8GA1UdDwEB/wQFAwMHsAAwHQYDVR0OBBYEFKrFhODjTKCopb+W oKqFYCw5FICvRgyh1KG8QWhW59Em0Jxr8rTw6qyBQACdixKy6/1ok2ArMivTC8Gd
Qa29PsHR4HXgMB8GA1UdIwQYMBaAFCyZxCa1ZUHVy8LjikE8zumAiEgfMA0GCSqG rEbefshgc6dnAZCAp1MjCU+tg9iYEymSSLdOtUKvHEIosUGO1p5ol0hReTQ=
SIb3DQEBCwUAA4IBgQBTrAgh6d+qiLumEfmkNCmhewxKxDZp+Ni2nz9XRzNO2cQE
U0n8MdbnQInW3xJXng2sAcl1fQz0RN1hkyjDwi69mbbPgcTYmxJFvyt+zRYBe/Sq
4CGGkxEdPW94tMpQ6SrCn2mAMnvcq9A1pYBVYyPeUsaRHC5OUBFOMCevNy8JwNyY
MJ0H5HQCyCysbzA1d521pogGUs/tmbE+ym9zpV8vG0b6De1PexjVeGkTNYz6NCR2
VZTQ+OJ5iE5pHPEC1Qif44LrR9Kdn/wu3RjTYyHeBOJFjK+DKgleNF4QVTcZQIPE
snN4H+/VSgTZQ3kgWbtpd1m5oRBJovEc2Qe+l+iDFCk8OA4z/x+fkvOeD3NUAl7D
9Pt3cP3UtWUJp4NJn2dvUljmQhB02HSqdNBhqKSg4/cf7l8Zo1ejvBUosrlgw3C3
apDaC4/xk7woFKVYW25teH2ze+Gpz/YsLDtmL7Bri8CGVsqsN9yqO8SstwKBa3Rt
xQ2em6XnnanApT4iFX4=
-----END CERTIFICATE----- -----END CERTIFICATE-----

View file

@ -0,0 +1,42 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,3ACC7169D177F0159193ACAF3B3997A3
DjxOUO2dbAAeHUtP2jSW/7zpVTWeRaJi5Kce74r1TB2DJ4FYI8361ZZcOrjISGQJ
33f1Ic+8gv3P5ORzGAIfxzSmwQLk5y45da7of2dj69FXba+WoGNKgMS/KMmj+CvR
XylNJl4RE5zovePkPvk2JDvyjg+POMMu3UTOoxJzSTmifV6F7msuFTHMHhs3edSs
PUAHprSW7Qh5dYq3VK8tuqg9qdy3uLajpZkg9b9bBfaiku+SiRfwsdCjeAuubiJK
ctyPQclE5B1jEgJit6odjzsLENB9uCzkgq61UPoxbT6URZ0jJwhEZgh15UAr74QP
KAElD8Q7V2Z0w31vPhBcMIyrSaNlMr5p4teNFlMEZRa0lhNOXp7AY0DwBtioX2bR
VCxFTk409L/gVaweUnS0jzY0cj/pU1L1I5OWScDjCRkkj0Vk40S/zcy5esz85b5r
rGRxdRKqJIIZeb3r7WdvINFnNXL/KL/hxVruZcZse8cV3Na+w4rH+AHElMd51tZ+
RKEBDqH0jlg3aelfAWXkV96pUtH/4lTSZ1+huQyHLUjTULll7L6BtxNGzY071buS
0CaTFyRcaipKYkXQjmrA49uTWQzrEgqiRZ4exh/gAaM/tEgVRfo/49Xo5wrTsGr0
4Q0hBnUYAa+cVL7K8z2WAk1qerb1CsmiyjQZFI1S6z10ugS6zTDdB/kwW5ZvAzWB
/DXc9rJlgTFLbZK7Oty/IDayYkWD3BjfOV94oMeogK0eworAMxhvfIFkPxRHwhIp
9KfBw7xsa2gJECbi8BvrsV69PHn6EHmphn7NMpc8A3KmBFv1uOqWu9P7ef67+e+U
JprzVt2mUDoTUayzVkwQPy3rm5wWxVanHqtRXig3RN3pnreEv1AdfTKLfCxE2jvo
9fh6hNo3urgIL1KFXHjiXVRt03RGfpWfAI3JKqhkWOqZ7rVT19AuJ6On2J1dVMkm
TFelKdX97YlvMfNdKp1pkzOjZ2f4ehL5WCkMq88VgDrTmZv+CfcnrRslsLP6MSpX
scAMFDdkzSBUH3NyHxxkstcs5xQm1SuPN/omB7rpYgfhD6HwdgZNEAINtMNgIIoR
tW34hGkV6BhI+2y+pkIndm63JVikrbuLKiwTjwynFJWKTWgRBMR/BvJ1Bq/IfJNo
pC/hIpN95vUbHGzHRfmO9v5HiaAaBYGs59gL6WS0OlsyFXMr6a9ZmBDbZ7TD94Ax
IAhGhRE+5OpF/kWLfOriXMEbyY/oNoN1y7jdpMdmncq2/26/OhL8RFUKPlCbz0LN
5FUv7ouW8kvUgy5tGu78iPu6MNI+BzqLg+TrUu2bufajS+/VGAFo/2PX896n+2FJ
cP2DXlmFgC6udIeWsGNJI8Y50fC+YZxN+UthLOctiOgM4pGK1UDl8JQLbt0xRrJA
MI5XkbXJJYBdjHaqg8WGF260UgWhlD9sdJc7ntLX9S+3DoOboSwmYu4Y8p15e4Cg
8LHgW4NmnBFPX5/oyYMVCt7SWEnnwGEeebu+YgD9fbFAsag5TpE04zpx58rCW5bh
sJqRBCcZE5rqO9CUF1fYu0F24fv+E3LK9lujCMARVfJk8CLUg7VFL9dY2XWEfHsO
plZ0lmc5BntBoQ5r+xK/6TbK5nn1Fo+JPRjnDaE++QdVx9ZVjtT+a/wCD5NJr13k
dByZ3eCz5+mZUBGD1PWh5C+iyL3Wpq29b3EsHfSIMzOZsCpY2jkC8Jr90ADxhZcH
j8wFXHIWCe+Nn89Zim53gvbzumspRj8Yb08RATruqpvwj3M/K5K6P92Lt3uqt6UB
W+tAcChHNNWHFIT5CtCV/rltJYe2c9k9yG6BZJeLWPYgq90dFkIqbdkiz/pVpmKS
WMMzvkaK+LEcv+M9eMUQPdPYWhwv67wAlUsdLVWyQtxoYcLPUY3Io+Smn8eE+Qz7
bxkSX+59QB3eCXrNGKTFsBiNDlxl+9YH6U9XhwIGyHlnBgN/79ts0ZutIpOibIWg
WPc9Cp5nkjjQl/4y8RSea8KSlkmM9YeTEo8cEL57XXOr1OO8UEPn/Ogoo5TI7JXL
jGh4evOcfWbiXZbn9kGshq1Kmv+lhN5IZ8QJY0s5Ze1eURnu0zlqKvFe3PxDxHV7
+PaM8MneRkT5B8QgC7prh/yJ0KEI2MyIcYP73fw8cOLTXenw0bpmKLLfxu8mSx4M
VEDqeZJUb/XwsZTd9VT+42p4YT/6wRAe9eU3zA9wKh4Sr96vUGTPktXcpxCjoBre
3IaF/6aeyRQn91Ps9XmOc0/KSxZmHMxWv2btVc4oLHawnyRlLXXT7OSG4FFR7eE8
IRoCCSip6YnIflp1v2n1f/07SzfKtrtVdiW1u2lbBJtwuzN/h8TtwRJan5bKWV/6
-----END RSA PRIVATE KEY-----

View file

@ -1,42 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-CBC,054A583F6C90570F
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
-----END RSA PRIVATE KEY-----

View file

@ -1,27 +1,26 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIEkDCCAvigAwIBAgIBFDANBgkqhkiG9w0BAQsFADBKMQ8wDQYDVQQDEwZUZXN0 MIIEeDCCAuCgAwIBAgIUTqTcNqmr8Ou/MpL1AUnM/3gcoUkwDQYJKoZIhvcNAQEL
ZXIxKjAoBgNVBAoTIUNTQUYgVG9vbHMgRGV2ZWxvcG1lbnQgKGludGVybmFsKTEL BQAwSjELMAkGA1UEBhMCREUxKjAoBgNVBAoTIUNTQUYgVG9vbHMgRGV2ZWxvcG1l
MAkGA1UEBhMCREUwHhcNMjMwOTA0MDcyMjAzWhcNMjMxMDI0MDcyMjAzWjBVMRow bnQgKGludGVybmFsKTEPMA0GA1UEAxMGVGVzdGVyMCAXDTI1MTAxNzEwMjIzNloY
GAYDVQQDExFUTFMgVGVzdCBDbGllbnQgMTEqMCgGA1UEChMhQ1NBRiBUb29scyBE DzIxMjUwOTIzMTAyMjM2WjBKMQswCQYDVQQGEwJERTEqMCgGA1UEChMhQ1NBRiBU
ZXZlbG9wbWVudCAoaW50ZXJuYWwpMQswCQYDVQQGEwJERTCCAaIwDQYJKoZIhvcN b29scyBEZXZlbG9wbWVudCAoaW50ZXJuYWwpMQ8wDQYDVQQDEwZUZXN0ZXIwggGi
AQEBBQADggGPADCCAYoCggGBAN0vZbLXtRzd61rR8Hos0BGnqCaJXIwGARwxJojM MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDBN4fIBbwuGJXjXoa6F7e4Zzin
yxASFT+KeC4QDRkgRrK6OY4k/i7TEHuUGk/Bm754++554wmmhDqv1Q4+VhhR1K/J Yd9EB4nt5TkNoMkRgQe0JIJ+t1/lS/xlI7ATxNjUdybnYwCrEfDvy8XGwN6te+Xh
Az/HVZNTAR1rPKwG82lyEpPxlRNZg/QtF9DqQSoSkL/fJLs+rq4zlKozXzREauZ5 dz6HKDWPijW+ritQW9kouxJJSpna95L8SqU4tjdfyL/2X9E/7j3VYw1//zcmhLJg
Be8So1dXRZfMVUMDgtk+IX8+iCeZisiWfv62ttQ0EiuiXLagd6ruEuoCSVi2tVsw 1Os0+JHPcPuj1vmwLa1v7eGTCNlt0K8DbrlhPlteJB3hWolNIoVDjRemZFmqwUeV
sC/Hp8AI2Ro56mmHiWthuae1H8yDWUFLSe9AQW65qC/xVUgo/nMpK2BYVFKb70TM GZ/XJos7OTB07p08yCOFhLl9jXCgEDDkKmcnAil3YhjudlEGSjdzFLskVD4xrtQ5
jl/dZM0Qn1tdiNyqCkbIhXjklZvZYhO+15TPkgDXDsqRUjpTrLZXLGrD6XIxCRLZ GsbdJHyHhcUdgh+vqX2bFSklwdwVil1qIUEHnxpcRMaluZQ4u1tCgNhKNQHrJzVQ
GY6YrUfsFTjUC6JrUrAR8zY7SLsYN5sUmFUSMpJnI+T/SD4p/0CXrKrbMOjWQqz6 n1aRVAYdX1PxfoIb5wt0+25MiVw8y8EcrMH97Ss26eNAtLeHZNrY9alqx/Cs8gOi
FX/WHPxvswGKHk5zHYGHrzx7OKmfVa6gzUgZSfOHj2xOOR2Un9DwNavIrmSCWYXK I8wA2Nga138tZuCJRXsDOnom9RrtdPLajhSb7n33Iq8ZDhYVGEIm2pc5MJxaI53V
Zqig5qDyfzBvlXWEio/5GrDwgQIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1Ud e2WhmemFPfYwUAtzdGgwrBoY9MechdtNLGZqHxECAwEAAaNUMFIwDAYDVR0TAQH/
JQQMMAoGCCsGAQUFBwMCMA8GA1UdDwEB/wQFAwMHoAAwHQYDVR0OBBYEFKrFhODj BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0O
TKCopb+WQa29PsHR4HXgMB8GA1UdIwQYMBaAFI6GhktAq9L2uRChC9LcXeedKiUg BBYEFBVaa/ovyPpbk/8nlmuISWB2/t8xMA0GCSqGSIb3DQEBCwUAA4IBgQC8EeDx
MA0GCSqGSIb3DQEBCwUAA4IBgQAbUDaIkmubooDde7BpZQx742BsPg4IN68bIg9A qipM7bAFxkAOmvhNAjodKXRCWKhatD8HryeINOPgWajzLlHj/PCnULulhaFO1viA
3jI9codx9c8l9ROvZ/7FeRNXzhYrQUwzcKpwtQ1mB7kM85oXaTLxrtnkZAO2fFSb +iBBKbHb+7LImb/owlNVu8iYDh/xBXmLrOHyd12K8dyN471iTBrskQwSCnYd6e/p
8RA6QjOrnOvewWaO3moCZaPnN1wWtlnUev2tD7D2Tz/f20dE2wbDV0BGb8bU4eGI 4i0hhNj5JidOgA6swjt9j4X7/IgsvXexLIAhqgQDSsKQpPK17E9IB+d5p3UHU71w
UVgzYrMh0MHaC8LKoXUWP97jp/p+9CG4D2S1CmpzP2Nm1dS03oj4UHIUtamjivYY Ob1mGIZ2j/GJnC6YmGFPqIZZ+cy3aVOypWf8RVZYPTFCz43ZuC70cP3kl2io75Rp
vOeoKATXmj59lgYqqoAVbTH6f4mZlZGmzUhRxK6hck7xBdiXAwfta72m4WzE7HRh rWUNKXU+yUdBphHN6KJXUmlH4T9yqXKqnxK+9CnVC/CTlucF9VpktN7wfVxVPsrY
nHAgO5aVWb6zltvVDJhYumB9Itv+LI7uU8fF9Uyc65SZ2BevxgikoDNxTx0oNr+4 L79iys+FLPKrDkqcjpIJ2l/n/ugcUcXvN477qFCGbRY/3tB3Dmf4AvMPpTsStkXw
hExQhJfKuPFF2NI1N2tPYJT53Cek/ZJfjX3TyBneqehthtRqoAIIEaF/QlXqzJIi Ld+xAHog8upjVGsmXODX4sKjRMIFLIHbM01Iw0ECdKoKIMwjFGenwGmpBZA/Pfxe
G66YFC3xFlLmaQh52DJkF2+hzcPhFTVQv3yCirGLUSS9Nm7vTO2wnnW5arZazSV+ AXBejd9KD0stCfHuKqx7Iu5N7Fg8BCLzmcSyoOmwJEo+Z3Z42IfSjOX8rQQ=
enRZb3oiVYFVDh0Hymz9g5VraMw=
-----END CERTIFICATE----- -----END CERTIFICATE-----

View file

@ -3,180 +3,180 @@ Public Key Info:
Key Security Level: High (3072 bits) Key Security Level: High (3072 bits)
modulus: modulus:
00:dd:2f:65:b2:d7:b5:1c:dd:eb:5a:d1:f0:7a:2c:d0 00:c1:37:87:c8:05:bc:2e:18:95:e3:5e:86:ba:17:b7
11:a7:a8:26:89:5c:8c:06:01:1c:31:26:88:cc:cb:10 b8:67:38:a7:61:df:44:07:89:ed:e5:39:0d:a0:c9:11
12:15:3f:8a:78:2e:10:0d:19:20:46:b2:ba:39:8e:24 81:07:b4:24:82:7e:b7:5f:e5:4b:fc:65:23:b0:13:c4
fe:2e:d3:10:7b:94:1a:4f:c1:9b:be:78:fb:ee:79:e3 d8:d4:77:26:e7:63:00:ab:11:f0:ef:cb:c5:c6:c0:de
09:a6:84:3a:af:d5:0e:3e:56:18:51:d4:af:c9:03:3f ad:7b:e5:e1:77:3e:87:28:35:8f:8a:35:be:ae:2b:50
c7:55:93:53:01:1d:6b:3c:ac:06:f3:69:72:12:93:f1 5b:d9:28:bb:12:49:4a:99:da:f7:92:fc:4a:a5:38:b6
95:13:59:83:f4:2d:17:d0:ea:41:2a:12:90:bf:df:24 37:5f:c8:bf:f6:5f:d1:3f:ee:3d:d5:63:0d:7f:ff:37
bb:3e:ae:ae:33:94:aa:33:5f:34:44:6a:e6:79:05:ef 26:84:b2:60:d4:eb:34:f8:91:cf:70:fb:a3:d6:f9:b0
12:a3:57:57:45:97:cc:55:43:03:82:d9:3e:21:7f:3e 2d:ad:6f:ed:e1:93:08:d9:6d:d0:af:03:6e:b9:61:3e
88:27:99:8a:c8:96:7e:fe:b6:b6:d4:34:12:2b:a2:5c 5b:5e:24:1d:e1:5a:89:4d:22:85:43:8d:17:a6:64:59
b6:a0:77:aa:ee:12:ea:02:49:58:b6:b5:5b:30:b0:2f aa:c1:47:95:19:9f:d7:26:8b:3b:39:30:74:ee:9d:3c
c7:a7:c0:08:d9:1a:39:ea:69:87:89:6b:61:b9:a7:b5 c8:23:85:84:b9:7d:8d:70:a0:10:30:e4:2a:67:27:02
1f:cc:83:59:41:4b:49:ef:40:41:6e:b9:a8:2f:f1:55 29:77:62:18:ee:76:51:06:4a:37:73:14:bb:24:54:3e
48:28:fe:73:29:2b:60:58:54:52:9b:ef:44:cc:8e:5f 31:ae:d4:39:1a:c6:dd:24:7c:87:85:c5:1d:82:1f:af
dd:64:cd:10:9f:5b:5d:88:dc:aa:0a:46:c8:85:78:e4 a9:7d:9b:15:29:25:c1:dc:15:8a:5d:6a:21:41:07:9f
95:9b:d9:62:13:be:d7:94:cf:92:00:d7:0e:ca:91:52 1a:5c:44:c6:a5:b9:94:38:bb:5b:42:80:d8:4a:35:01
3a:53:ac:b6:57:2c:6a:c3:e9:72:31:09:12:d9:19:8e eb:27:35:50:9f:56:91:54:06:1d:5f:53:f1:7e:82:1b
98:ad:47:ec:15:38:d4:0b:a2:6b:52:b0:11:f3:36:3b e7:0b:74:fb:6e:4c:89:5c:3c:cb:c1:1c:ac:c1:fd:ed
48:bb:18:37:9b:14:98:55:12:32:92:67:23:e4:ff:48 2b:36:e9:e3:40:b4:b7:87:64:da:d8:f5:a9:6a:c7:f0
3e:29:ff:40:97:ac:aa:db:30:e8:d6:42:ac:fa:15:7f ac:f2:03:a2:23:cc:00:d8:d8:1a:d7:7f:2d:66:e0:89
d6:1c:fc:6f:b3:01:8a:1e:4e:73:1d:81:87:af:3c:7b 45:7b:03:3a:7a:26:f5:1a:ed:74:f2:da:8e:14:9b:ee
38:a9:9f:55:ae:a0:cd:48:19:49:f3:87:8f:6c:4e:39 7d:f7:22:af:19:0e:16:15:18:42:26:da:97:39:30:9c
1d:94:9f:d0:f0:35:ab:c8:ae:64:82:59:85:ca:66:a8 5a:23:9d:d5:7b:65:a1:99:e9:85:3d:f6:30:50:0b:73
a0:e6:a0:f2:7f:30:6f:95:75:84:8a:8f:f9:1a:b0:f0 74:68:30:ac:1a:18:f4:c7:9c:85:db:4d:2c:66:6a:1f
81: 11:
public exponent: public exponent:
01:00:01: 01:00:01:
private exponent: private exponent:
14:ff:c0:f9:ff:bc:b4:26:e5:87:53:d3:2e:e6:3e:42 70:0e:fd:af:d3:2b:ad:6c:52:d9:f8:43:99:00:12:6c
ce:d6:0a:02:94:84:be:b5:30:46:02:50:8e:90:e0:cf 5f:69:2b:22:87:33:54:4f:f9:69:fc:e9:db:7b:61:ac
b6:b0:b7:a6:bd:48:cc:d5:8b:d8:ea:72:ff:af:dd:17 7c:c4:4c:7c:66:73:81:a9:61:a5:73:1e:fc:8a:aa:9a
3c:be:d1:1b:ca:6d:cd:10:a6:86:a8:d9:d2:44:44:27 ba:b6:94:18:94:81:99:b5:a1:0f:e2:15:c5:4c:ac:98
d0:65:51:65:0c:27:34:07:dc:7b:38:64:10:03:7c:f4 df:07:96:f8:ea:89:c6:97:31:b5:8d:b0:16:21:46:cc
a1:cd:40:de:24:3a:e0:21:bc:ef:33:1d:9f:61:e8:57 ce:28:62:3e:9b:c5:29:70:26:2f:d8:24:8e:a8:52:7d
ac:e4:9c:c0:7b:df:7c:f8:20:83:ac:0b:8e:0b:d3:62 d1:0e:83:ce:a7:09:9b:d3:57:87:3f:98:5f:c8:ab:ba
eb:8a:8e:03:5b:a3:e5:08:ae:df:a7:fe:85:92:e8:a5 aa:31:2e:19:ae:84:1d:39:ab:9e:b2:42:f6:75:ff:68
ae:58:46:72:d6:fc:91:43:b1:7b:a4:c0:5f:51:c3:50 ae:73:00:fa:d7:a4:c5:3d:7c:4f:54:65:4e:1c:88:e6
0d:e2:67:e8:af:51:13:41:a9:8d:ef:fb:a1:a4:e2:84 c2:b5:9d:a2:ca:38:61:45:09:17:01:68:5a:f7:4e:4d
7c:2b:a0:50:c5:fe:ed:84:a5:25:83:86:4a:d3:0f:56 cb:24:f1:e3:57:a1:97:58:1e:b3:ef:57:91:e0:1d:95
37:38:e6:1e:26:7d:45:22:0b:ba:22:35:be:f8:8b:1b 51:8c:a9:4a:4e:f7:cd:fe:f7:04:f3:ff:67:ad:e7:01
72:90:13:c4:1f:c5:d1:34:b5:0e:b2:ee:f7:e1:b9:5e 14:dc:7e:e4:00:c0:38:51:2f:04:db:39:6c:f1:1b:a4
a2:29:8d:f9:6e:23:4b:50:8f:35:c8:a9:f3:d2:1f:dd a5:f1:b4:5a:c3:17:d2:41:1a:5a:b5:f3:69:3b:b8:ba
ce:a0:96:50:2d:2e:af:cf:b5:e1:20:e7:e9:d2:49:ed 7b:59:96:d7:b2:c2:2c:9a:dd:e9:42:ce:fb:c8:22:fc
b5:0e:5b:3e:d1:4b:f1:fa:c2:73:3a:1b:51:34:7e:75 c5:33:97:6d:68:89:cd:e5:bc:2e:cc:9d:23:65:18:04
30:06:d2:47:d2:a8:2a:45:be:16:fb:8f:63:84:85:b7 0c:83:b6:35:7e:16:09:96:d1:48:61:31:b1:ce:f8:50
bf:f7:c4:c5:3d:95:56:8c:d1:02:7f:58:ac:4d:11:7b f0:14:ba:57:2f:02:1b:61:9c:bc:81:c1:ef:b3:bf:2f
c5:55:f3:c8:4e:d7:d9:aa:62:b0:e3:1e:04:5c:97:d1 fb:36:af:18:8c:90:40:55:5a:fd:a7:d4:ed:3b:94:a6
ca:e2:71:aa:8b:33:b4:34:e9:04:d4:70:7c:f4:cb:57 df:ab:eb:6c:d2:bc:e3:80:7e:d5:06:21:28:9b:04:65
19:c1:03:23:f4:bc:4d:91:8f:b2:9a:99:1c:6c:81:2d b5:cc:04:b2:44:e9:2d:3b:7d:de:24:90:8d:fb:90:2d
4d:2d:e9:a1:e3:ce:e3:c9:62:52:89:1f:47:86:61:f1 40:17:51:cf:a7:fa:ee:54:89:8f:c0:f4:e4:c2:bd:44
dd:bc:46:8d:79:0a:99:9d:aa:4b:a9:0a:72:54:db:dc 94:1d:8d:fc:b7:d7:05:4d:46:dc:63:1f:7f:d8:b4:8b
ae:48:be:60:4a:73:99:d8:3c:9e:07:78:05:df:87:39 11:db:37:be:4d:e9:2b:33:b9:6b:8c:a7:f0:43:56:c5
prime1: prime1:
00:e9:63:0f:d7:49:31:27:a8:36:fe:95:bd:8d:05:c1 00:f0:57:25:fd:aa:7e:98:13:08:28:99:16:eb:af:2e
35:48:2e:03:4f:a6:57:54:3a:a4:95:3f:8e:9f:28:7c 22:f6:e6:d7:bd:df:49:57:17:71:bf:21:ba:bf:75:54
d2:df:af:54:36:9e:7c:9f:c3:b9:64:8f:c0:b0:96:3c 5a:38:92:64:8c:4a:10:d4:4f:77:18:44:c2:79:f0:9d
aa:01:f6:9a:be:83:e2:85:20:0d:33:de:88:97:af:6f 72:26:2e:9a:27:5d:e7:41:0b:c6:65:cb:fa:89:6d:9b
be:3f:53:5a:a3:77:02:fd:81:17:91:3b:b2:2d:ab:78 fb:87:78:e2:87:22:d4:92:21:f5:3a:57:fa:b0:bf:bb
db:d9:43:db:04:69:82:61:30:e4:96:ac:88:8b:f6:3f 66:a2:bf:43:af:e8:58:b4:e2:a1:ed:97:62:09:0d:49
56:c4:49:fd:d5:e5:8c:9d:30:ad:cf:d9:8d:5c:87:b5 ca:4c:99:a2:f4:f3:31:df:80:8e:56:be:64:9d:72:59
27:4b:09:8e:19:ed:e2:11:3f:69:b2:47:be:70:39:11 ef:e9:db:4d:a3:e2:cf:79:1e:99:89:b2:f1:e3:2d:bc
41:a3:db:bb:b9:0e:e4:7b:50:d0:d2:c2:89:81:36:b9 8f:a0:2a:2f:a6:f0:21:18:2d:f1:57:20:55:c1:c9:18
6b:a6:fe:94:5b:06:66:e6:ed:86:52:42:5e:a9:0e:18 c1:64:c6:9c:00:df:b2:54:55:8d:fe:d3:46:a0:5c:2e
db:18:f9:14:21:3d:e0:3c:8d:79:c3:f5:d2:cc:51:65 f8:f7:10:b6:27:3a:4a:79:a1:14:b1:0c:c3:72:5b:2b
fb:1c:49:ed:0a:d5:33:99:34:16:f9:1d:68:4a:78:da 66:d6:85:2c:7e:58:72:eb:33:62:73:34:e5:38:87:2e
5f: 17:
prime2: prime2:
00:f2:9d:ae:5f:bd:b7:a3:87:a7:8d:30:46:06:8b:15 00:cd:ce:5d:fb:04:16:34:f4:de:02:7d:00:07:3e:b0
a9:e5:a9:58:1c:2b:3a:7e:78:35:36:56:31:42:df:46 94:8c:f4:3a:62:05:37:1a:4f:d8:40:2e:31:11:07:77
87:e8:57:0d:6e:99:de:cf:fb:a8:72:16:71:4b:b3:ad 09:8b:bd:76:6e:85:b9:43:df:3f:86:cb:db:6d:fe:c6
ed:74:07:cb:cf:7d:2b:12:89:66:c4:0f:8a:ea:e3:37 4c:ca:e1:16:ce:5c:0e:e1:b1:10:0d:8d:48:99:d7:43
17:2c:75:92:11:7a:a6:da:29:24:33:9b:69:c2:64:68 7f:6c:b6:20:b2:cd:0c:56:26:02:18:81:e1:67:e5:cd
03:db:31:de:fe:1d:a2:4d:9d:91:9f:f0:50:b8:8f:d0 b3:66:1e:77:dc:49:6a:5d:8c:9c:0e:24:14:3e:a1:4a
22:11:b9:b0:95:98:5e:65:bf:45:97:9b:35:f2:98:27 7e:cf:72:e6:e4:03:e6:38:41:fa:2b:91:71:6c:33:b0
46:7c:b2:86:eb:7b:8b:57:f2:c3:49:47:7d:01:4a:9a ec:07:3a:be:5b:f8:74:f5:e4:1f:9c:c4:d0:d4:75:a8
b0:e6:67:05:e5:61:7a:ab:63:c8:cb:d8:44:69:88:72 35:09:05:0f:7f:54:4e:2a:bc:cc:92:de:1e:f4:74:8a
a5:a9:60:89:60:df:e6:d9:4d:16:2b:35:7b:20:00:f3 56:36:e0:b1:37:cf:b3:9c:57:05:76:59:69:c3:03:de
3c:d1:78:f9:22:eb:48:c3:7f:78:63:e6:34:60:48:30 c2:33:0c:c4:a1:4f:2a:b8:3c:20:63:c9:58:96:1a:e2
66:02:bb:38:c2:94:2e:b9:86:b2:2f:9a:4f:17:7f:e1 62:ce:bf:fb:a9:51:b0:66:99:35:d6:d2:60:59:72:bd
1f: 17:
coefficient: coefficient:
00:93:3e:7c:b9:ea:87:52:37:fa:d5:0a:36:fb:e1:d0 33:6a:05:3e:1e:46:46:58:e2:61:38:6a:c2:8f:77:a2
fc:62:4d:00:0b:ad:a8:fb:bd:34:53:96:c2:6c:a1:6a 27:b7:19:38:75:40:d6:8c:87:bc:65:a6:24:c3:97:e5
49:b7:a0:24:33:16:95:79:14:ac:bb:75:8d:78:e9:10 ef:70:1b:2c:4e:9c:08:ca:1d:eb:97:11:74:14:bb:99
fa:be:44:60:58:94:4a:9c:ba:64:1d:86:27:8b:7f:51 de:22:a1:6e:bc:6c:c6:25:98:8a:8e:17:f4:f9:4d:a3
4d:80:b0:ff:7a:91:c0:4d:a4:aa:d1:f1:79:7d:8f:71 1d:01:5e:26:0e:b4:e8:1c:aa:06:7c:66:b1:89:5a:b4
49:12:73:d4:44:5f:0c:2e:55:a6:d9:13:b8:3b:e5:dc 82:65:d1:bf:20:cb:b2:57:a8:af:7f:00:07:00:7c:5e
e1:14:98:7e:eb:5b:60:ad:d7:4b:da:c0:d8:3f:bf:70 d4:09:60:0c:0a:6e:a8:e1:16:1b:04:95:b1:bc:2b:35
92:53:8c:31:6a:8b:61:5e:a3:7d:ff:84:2c:7d:ed:9f ad:80:78:0a:0a:1d:5f:c9:cc:24:3a:5e:20:03:50:44
74:29:9a:e7:14:fb:c3:ab:8e:9f:60:6a:98:ab:86:0b b8:b0:f3:f1:17:ff:41:b8:5d:56:9b:1c:f1:e6:2b:c6
ea:fb:ff:20:2f:3b:a7:76:03:3a:55:bb:b2:c6:9c:b5 ba:a2:8c:18:25:8c:d5:90:f1:28:66:29:bb:40:3d:b2
66:36:b8:1c:7f:9b:b6:62:89:ff:6a:d6:35:58:0b:f0 f9:65:99:2e:b7:1b:e3:d0:d2:1a:d7:96:70:cc:f6:74
55:27:01:f0:67:8d:88:3f:74:48:3d:bf:8c:fc:05:62 c5:2e:bf:f5:c9:60:c0:ff:38:f8:a8:db:1a:7d:6a:4e
47:
exp1: exp1:
00:99:16:2d:91:dd:a4:ac:8a:9e:68:27:f8:89:c4:38 5c:1b:49:f7:f9:0b:23:04:c8:2f:a6:db:dd:de:f8:f3
93:a6:a0:e7:f3:1a:fd:35:76:b1:f6:64:16:3d:37:e5 75:63:ea:72:5d:cc:21:90:5e:8b:3d:45:f0:71:ea:ad
88:bc:c8:d8:c8:6a:f4:fc:26:fa:38:88:42:b0:92:1b d8:d8:61:a8:52:0a:39:13:6b:34:e5:c5:12:2e:60:68
80:b8:80:f5:c7:f9:e2:5f:c8:42:60:bf:9b:81:43:c6 8a:b1:79:6a:74:d6:57:5b:47:e1:63:56:d4:ac:29:07
5c:58:55:68:a2:c8:b1:e1:6f:07:f2:6f:e1:d4:2b:21 30:57:e7:98:9a:84:94:ac:66:ea:c1:24:d5:ef:e4:c5
bf:b3:a7:da:c5:ee:1f:63:79:1a:b7:ea:bc:36:72:73 e4:c1:20:13:9e:1b:c0:d6:c9:ef:e0:00:36:2f:dd:83
e1:8a:27:ae:a4:db:49:7c:e2:2d:60:a5:27:20:86:b3 a5:ef:8b:40:0c:a3:a4:60:04:2c:c2:32:95:14:69:db
c0:ee:6b:7a:16:6f:ff:55:a8:ee:bf:ce:67:90:5d:1e 43:e8:43:cc:f6:f3:44:1b:b2:03:cf:8c:5b:df:ff:4f
80:9b:e6:ca:1f:fd:30:c9:e2:9c:d7:62:5b:a7:b2:29 9b:b6:0f:25:0f:09:df:d6:5b:93:64:54:f9:3b:34:3d
b5:ff:78:06:00:1f:16:e8:6a:ed:2c:8f:f4:5f:97:ab 89:7d:83:f3:e1:c6:da:03:1f:b3:f5:0c:30:10:a3:ff
9e:2b:a7:56:18:e7:e9:6a:4e:b2:8c:63:76:be:26:b6 cd:cf:9d:bf:52:db:8f:d9:67:b0:a2:8f:94:97:d3:fe
6a:1c:88:31:40:65:d0:ce:b1:68:50:47:85:dd:33:a0 49:60:28:39:13:74:97:26:ce:28:10:b1:78:04:76:69
a9:
exp2: exp2:
00:8d:b1:5f:7c:94:ed:62:39:40:b6:a9:a1:cc:02:80 6e:6d:c5:d5:b3:8a:aa:dd:9c:e6:5e:e6:0d:fd:20:48
c5:77:d6:9e:19:dd:79:4d:11:61:6a:79:8e:4d:92:de 85:1d:62:da:47:8c:1a:8d:2f:2e:b8:da:51:15:dd:54
bb:53:0b:3c:52:02:d5:69:3c:7d:95:1b:dc:51:2d:00 7c:eb:ab:49:80:6d:39:32:e7:e6:4f:2a:2d:6a:20:43
00:35:0a:b4:92:5a:74:c4:5f:b0:c0:02:9f:cc:2c:a5 02:35:26:c4:91:76:d6:b8:e8:31:2d:57:00:5d:15:f5
29:08:93:25:9a:c5:ba:1a:a1:7a:7e:15:5e:ff:e3:ea a0:82:55:27:3b:88:dc:0c:c6:e1:19:87:b5:f5:03:9b
07:8e:85:a2:c9:60:7f:40:bb:2c:a8:6f:0e:85:ab:a0 b8:36:ae:ff:bf:50:d8:63:63:34:df:3d:11:a1:ff:d3
0f:b5:b0:70:1b:fe:1f:eb:66:78:fb:60:ef:71:de:40 ed:41:ed:0b:f9:df:a4:de:19:fb:18:ae:70:6d:88:08
d9:de:cb:d9:16:40:52:12:2c:3a:b7:5a:63:fc:54:18 0d:95:02:a1:5c:be:7d:55:eb:74:75:d2:cb:bd:5a:05
e2:05:bd:d7:68:ae:b4:98:d2:2f:1c:36:13:46:5b:25 23:12:d9:0e:ec:50:88:f4:07:1c:e3:1c:5e:f4:cd:69
31:f1:28:eb:32:c3:b1:2b:e9:e4:6f:99:cd:6d:d4:80 97:46:97:30:a8:3c:ea:ad:72:db:de:fc:35:cc:b4:d1
3a:5d:d0:3c:18:93:b7:2c:4e:0e:fe:b1:1c:97:ba:b1 25:0d:3b:d0:86:27:18:f6:02:37:28:c9:64:b9:86:31
61:72:68:eb:6e:60:62:a5:81:b0:21:33:0a:cc:1b:a8 98:58:41:13:c8:26:4b:d6:f7:a1:8d:fe:6e:e0:76:ff
5b:
Public Key PIN: Public Key PIN:
pin-sha256:iFdBnKP/7hZCLdj7qqTtdNPFjpZGka259fSYvv3X02U= pin-sha256:Zv2mSFRUYM7ofg5obMJJxhZpnuvO7gkCOlqfDK1gzks=
Public Key ID: Public Key ID:
sha256:8857419ca3ffee16422dd8fbaaa4ed74d3c58e964691adb9f5f498befdd7d365 sha256:66fda648545460cee87e0e686cc249c616699eebceee09023a5a9f0cad60ce4b
sha1:aac584e0e34ca0a8a5bf9641adbd3ec1d1e075e0 sha1:155a6bfa2fc8fa5b93ff27966b88496076fedf31
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----

37
internal/misc/json.go Normal file
View file

@ -0,0 +1,37 @@
// This file is Free Software under the Apache-2.0 License
// without warranty, see README.md and LICENSES/Apache-2.0.txt for details.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileCopyrightText: 2025 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2025 Intevation GmbH <https://intevation.de>
package misc
import (
"encoding/json"
"fmt"
"io"
)
// StrictJSONParse creates a JSON decoder that decodes an interface
// while not allowing trailing data
func StrictJSONParse(jsonData io.Reader, target any) error {
decoder := json.NewDecoder(jsonData)
// Don't allow unknown fields
decoder.DisallowUnknownFields()
if err := decoder.Decode(target); err != nil {
return fmt.Errorf("JSON decoding error: %w", err)
}
// Check for any trailing data after the main JSON structure
if _, err := decoder.Token(); err != io.EOF {
if err != nil {
return fmt.Errorf("error reading trailing data: %w", err)
}
return fmt.Errorf("unexpected trailing data after JSON object")
}
return nil
}

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2023 Intevation GmbH <https://intevation.de> // Software-Engineering: 2023 Intevation GmbH <https://intevation.de>
package misc package misc //revive:disable-line:var-naming
import ( import (
"fmt" "fmt"

21
internal/misc/url.go Normal file
View file

@ -0,0 +1,21 @@
// This file is Free Software under the Apache-2.0 License
// without warranty, see README.md and LICENSES/Apache-2.0.txt for details.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileCopyrightText: 2025 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2025 Intevation GmbH <https://intevation.de>
package misc
import "net/url"
// JoinURL joins the two URLs while preserving the query and fragment part of the latter.
func JoinURL(baseURL *url.URL, relativeURL *url.URL) *url.URL {
u := baseURL.JoinPath(relativeURL.Path)
u.RawQuery = relativeURL.RawQuery
u.RawFragment = relativeURL.RawFragment
// Enforce https, this is required if the base url was only a domain
u.Scheme = "https"
return u
}

View file

@ -81,7 +81,7 @@ func TestUnmarshalText(t *testing.T) {
byteSlice := []byte{'3', 'h'} byteSlice := []byte{'3', 'h'}
var emptySlice []byte var emptySlice []byte
if testTimeRange.UnmarshalText(byteSlice) != nil { if testTimeRange.UnmarshalText(byteSlice) != nil {
t.Errorf(testTimeRange.UnmarshalText(byteSlice).Error()) t.Error(testTimeRange.UnmarshalText(byteSlice).Error())
} }
if testTimeRange.UnmarshalText(emptySlice) == nil { if testTimeRange.UnmarshalText(emptySlice) == nil {
t.Errorf("Failure: UnmarshalText succeeded on invalid slice of bytes.") t.Errorf("Failure: UnmarshalText succeeded on invalid slice of bytes.")
@ -104,10 +104,10 @@ func TestUnmarshalFlag(t *testing.T) {
time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC)) time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC))
if err := testTimeRange.UnmarshalFlag("3h"); err != nil { if err := testTimeRange.UnmarshalFlag("3h"); err != nil {
t.Errorf(err.Error()) t.Error(err.Error())
} }
if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05"); err != nil { if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05"); err != nil {
t.Errorf(err.Error()) t.Error(err.Error())
} }
if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05a"); err == nil { if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05a"); err == nil {
t.Errorf("Failure: Extracted time from invalid string") t.Errorf("Failure: Extracted time from invalid string")
@ -119,7 +119,7 @@ func TestUnmarshalFlag(t *testing.T) {
t.Errorf("Failure: Extracted time from invalid string") t.Errorf("Failure: Extracted time from invalid string")
} }
if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05, 2007-01-02T15:04:05"); err != nil { if err := testTimeRange.UnmarshalFlag("2006-01-02T15:04:05, 2007-01-02T15:04:05"); err != nil {
t.Errorf(err.Error()) t.Error(err.Error())
} }
} }

View file

@ -46,7 +46,6 @@ type Parser[C any] struct {
// If a config file was specified it is loaded. // If a config file was specified it is loaded.
// Returns the arguments and the configuration. // Returns the arguments and the configuration.
func (p *Parser[C]) Parse() ([]string, *C, error) { func (p *Parser[C]) Parse() ([]string, *C, error) {
var cmdLineOpts C var cmdLineOpts C
if p.SetDefaults != nil { if p.SetDefaults != nil {
p.SetDefaults(&cmdLineOpts) p.SetDefaults(&cmdLineOpts)
@ -82,6 +81,7 @@ func (p *Parser[C]) Parse() ([]string, *C, error) {
// No config file -> We are good. // No config file -> We are good.
if path == "" { if path == "" {
slog.Warn("No config file found. Maybe you want to specify one or store it in a respective default location", "locations", p.DefaultConfigLocations)
return args, &cmdLineOpts, nil return args, &cmdLineOpts, nil
} }

View file

@ -90,7 +90,7 @@ func TestParse(t *testing.T) {
cmd.Env = append(os.Environ(), "TEST_HELP=1") cmd.Env = append(os.Environ(), "TEST_HELP=1")
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatal(err.Error())
} }
// test the version flag // test the version flag
@ -104,7 +104,7 @@ func TestParse(t *testing.T) {
cmd.Env = append(os.Environ(), "TEST_VERSION=1") cmd.Env = append(os.Environ(), "TEST_VERSION=1")
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatal(err.Error())
} }
} }
@ -140,7 +140,7 @@ func TestLoadToml(t *testing.T) {
t.Errorf("Failure: Succeeded in parsing nonexistant parameter") t.Errorf("Failure: Succeeded in parsing nonexistant parameter")
} }
if err := loadTOML(&cfg, "data/config.toml"); err != nil { if err := loadTOML(&cfg, "data/config.toml"); err != nil {
t.Errorf(err.Error()) t.Error(err.Error())
} }
} }

View file

@ -0,0 +1,171 @@
{
"document": {
"category": "csaf_vex",
"csaf_version": "2.0",
"distribution": {
"tlp": {
"label": "WHITE",
"url": "https://www.first.org/tlp/v1/"
}
},
"notes": [
{
"category": "summary",
"title": "Test document summary",
"text": "Auto generated test CSAF document"
}
],
"publisher": {
"category": "vendor",
"name": "ACME Inc.",
"namespace": "https://www.example.com"
},
"title": "Test CSAF document",
"tracking": {
"current_release_date": "2020-01-01T00:00:00Z",
"generator": {
"date": "2020-01-01T00:00:00Z",
"engine": {
"name": "csaf-tool",
"version": "0.3.2"
}
},
"id": "Avendor-advisory-0004",
"initial_release_date": "2020-01-01T00:00:00Z",
"revision_history": [
{
"date": "2020-01-01T00:00:00Z",
"number": "1",
"summary": "Initial version"
}
],
"status": "final",
"version": "1"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_1",
"branches": [
{
"category": "product_version",
"name": "1.1",
"product": {
"name": "AVendor product_1 1.1",
"product_id": "CSAFPID_0001"
}
},
{
"category": "product_version",
"name": "1.2",
"product": {
"name": "AVendor product_1 1.2",
"product_id": "CSAFPID_0002"
}
},
{
"category": "product_version",
"name": "2.0",
"product": {
"name": "AVendor product_1 2.0",
"product_id": "CSAFPID_0003"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor1",
"branches": [
{
"category": "product_name",
"name": "product_2",
"branches": [
{
"category": "product_version",
"name": "1",
"product": {
"name": "AVendor1 product_2 1",
"product_id": "CSAFPID_0004"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_3",
"branches": [
{
"category": "product_version",
"name": "2022H2",
"product": {
"name": "AVendor product_3 2022H2",
"product_id": "CSAFPID_0005"
}
}
]
}
]
}
]
},
"vulnerabilities": [
{
"cve": "CVE-2020-1234",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Customers should upgrade to the latest version of the product",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
},
{
"cve": "CVE-2020-9876",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Still under investigation",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
}
]
}
invalid data

View file

@ -0,0 +1,169 @@
{
"document": {
"category": "csaf_vex",
"csaf_version": "2.0",
"distribution": {
"tlp": {
"label": "WHITE",
"url": "https://www.first.org/tlp/v1/"
}
},
"notes": [
{
"category": "summary",
"title": "Test document summary",
"text": "Auto generated test CSAF document"
}
],
"publisher": {
"category": "vendor",
"name": "ACME Inc.",
"namespace": "https://www.example.com"
},
"title": "Test CSAF document",
"tracking": {
"current_release_date": "2020-01-01T00:00:00Z",
"generator": {
"date": "2020-01-01T00:00:00Z",
"engine": {
"name": "csaf-tool"
}
},
"id": "Avendor-advisory-0004",
"initial_release_date": "2020-01-01T00:00:00Z",
"revision_history": [
{
"date": "2020-01-01T00:00:00Z",
"number": "1",
"summary": "Initial version"
}
],
"status": "final",
"version": "1"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_1",
"branches": [
{
"category": "product_version",
"name": "1.1",
"product": {
"name": "AVendor product_1 1.1",
"product_id": "CSAFPID_0001"
}
},
{
"category": "product_version",
"name": "1.2",
"product": {
"name": "AVendor product_1 1.2",
"product_id": "CSAFPID_0002"
}
},
{
"category": "product_version",
"name": "2.0",
"product": {
"name": "AVendor product_1 2.0",
"product_id": "CSAFPID_0003"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor1",
"branches": [
{
"category": "product_name",
"name": "product_2",
"branches": [
{
"category": "product_version",
"name": "1",
"product": {
"name": "AVendor1 product_2 1",
"product_id": "CSAFPID_0004"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_3",
"branches": [
{
"category": "product_version",
"name": "2022H2",
"product": {
"name": "AVendor product_3 2022H2",
"product_id": "CSAFPID_0005"
}
}
]
}
]
}
]
},
"vulnerabilities": [
{
"cve": "CVE-2020-1234",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Customers should upgrade to the latest version of the product",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
},
{
"cve": "CVE-2020-9876",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Still under investigation",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
}
]
}

View file

@ -0,0 +1,170 @@
{
"document": {
"category": "csaf_vex",
"csaf_version": "2.0",
"distribution": {
"tlp": {
"label": "WHITE",
"url": "https://www.first.org/tlp/v1/"
}
},
"notes": [
{
"category": "summary",
"title": "Test document summary",
"text": "Auto generated test CSAF document"
}
],
"publisher": {
"category": "vendor",
"name": "ACME Inc.",
"namespace": "https://www.example.com"
},
"title": "Test CSAF document",
"tracking": {
"current_release_date": "2020-01-01T00:00:00Z",
"generator": {
"date": "2020-01-01T00:00:00Z",
"engine": {
"name": "csaf-tool",
"version": "0.3.2"
}
},
"id": "Avendor-advisory-0004",
"initial_release_date": "2020-01-01T00:00:00Z",
"revision_history": [
{
"date": "2020-01-01T00:00:00Z",
"number": "1",
"summary": "Initial version"
}
],
"status": "final",
"version": "1"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_1",
"branches": [
{
"category": "product_version",
"name": "1.1",
"product": {
"name": "AVendor product_1 1.1",
"product_id": "CSAFPID_0001"
}
},
{
"category": "product_version",
"name": "1.2",
"product": {
"name": "AVendor product_1 1.2",
"product_id": "CSAFPID_0002"
}
},
{
"category": "product_version",
"name": "2.0",
"product": {
"name": "AVendor product_1 2.0",
"product_id": "CSAFPID_0003"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor1",
"branches": [
{
"category": "product_name",
"name": "product_2",
"branches": [
{
"category": "product_version",
"name": "1",
"product": {
"name": "AVendor1 product_2 1",
"product_id": "CSAFPID_0004"
}
}
]
}
]
},
{
"category": "vendor",
"name": "AVendor",
"branches": [
{
"category": "product_name",
"name": "product_3",
"branches": [
{
"category": "product_version",
"name": "2022H2",
"product": {
"name": "AVendor product_3 2022H2",
"product_id": "CSAFPID_0005"
}
}
]
}
]
}
]
},
"vulnerabilities": [
{
"cve": "CVE-2020-1234",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Customers should upgrade to the latest version of the product",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
},
{
"cve": "CVE-2020-9876",
"notes": [
{
"category": "description",
"title": "CVE description",
"text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876"
}
],
"product_status": {
"under_investigation": ["CSAFPID_0001"]
},
"threats": [
{
"category": "impact",
"details": "Still under investigation",
"date": "2020-01-01T00:00:00Z",
"product_ids": ["CSAFPID_0001"]
}
]
}
]
}

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2022 Intevation GmbH <https://intevation.de> // Software-Engineering: 2022 Intevation GmbH <https://intevation.de>
package util package util //revive:disable-line:var-naming
import ( import (
"context" "context"

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2022 Intevation GmbH <https://intevation.de> // Software-Engineering: 2022 Intevation GmbH <https://intevation.de>
package util package util //revive:disable-line:var-naming
import ( import (
"bufio" "bufio"

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2022 Intevation GmbH <https://intevation.de> // Software-Engineering: 2022 Intevation GmbH <https://intevation.de>
package util package util //revive:disable-line:var-naming
import ( import (
"bytes" "bytes"

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2023 Intevation GmbH <https://intevation.de> // Software-Engineering: 2023 Intevation GmbH <https://intevation.de>
package util package util //revive:disable-line:var-naming
// Set is a simple set type. // Set is a simple set type.
type Set[K comparable] map[K]struct{} type Set[K comparable] map[K]struct{}

View file

@ -6,7 +6,7 @@
// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de> // SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2022 Intevation GmbH <https://intevation.de> // Software-Engineering: 2022 Intevation GmbH <https://intevation.de>
package util package util //revive:disable-line:var-naming
import ( import (
"net/url" "net/url"