Compare commits
No commits in common. "master" and "v0.2.0" have entirely different histories.
6 changed files with 76 additions and 274 deletions
|
|
@ -1,22 +0,0 @@
|
||||||
image: happydomain/checker-email-keys:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
|
||||||
{{#if build.tags}}
|
|
||||||
tags:
|
|
||||||
{{#each build.tags}}
|
|
||||||
- {{this}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
manifests:
|
|
||||||
- image: happydomain/checker-email-keys:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
|
||||||
platform:
|
|
||||||
architecture: amd64
|
|
||||||
os: linux
|
|
||||||
- image: happydomain/checker-email-keys:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
|
||||||
platform:
|
|
||||||
architecture: arm64
|
|
||||||
os: linux
|
|
||||||
variant: v8
|
|
||||||
- image: happydomain/checker-email-keys:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
|
||||||
platform:
|
|
||||||
architecture: arm
|
|
||||||
os: linux
|
|
||||||
variant: v7
|
|
||||||
187
.drone.yml
187
.drone.yml
|
|
@ -1,187 +0,0 @@
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build-amd64
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: checker build
|
|
||||||
image: golang:1-alpine
|
|
||||||
commands:
|
|
||||||
- apk add --no-cache git make
|
|
||||||
- make
|
|
||||||
environment:
|
|
||||||
CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}"
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: checker build tag
|
|
||||||
image: golang:1-alpine
|
|
||||||
commands:
|
|
||||||
- apk add --no-cache git make
|
|
||||||
- make
|
|
||||||
environment:
|
|
||||||
CHECKER_VERSION: "${DRONE_SEMVER}"
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: publish on Docker Hub
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
repo: happydomain/checker-email-keys
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
build_args:
|
|
||||||
- CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT}
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: publish on Docker Hub (tag)
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
repo: happydomain/checker-email-keys
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
build_args:
|
|
||||||
- CHECKER_VERSION=${DRONE_SEMVER}
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
exclude:
|
|
||||||
- renovate/*
|
|
||||||
event:
|
|
||||||
- cron
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build-arm64
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: checker build
|
|
||||||
image: golang:1-alpine
|
|
||||||
commands:
|
|
||||||
- apk add --no-cache git make
|
|
||||||
- make
|
|
||||||
environment:
|
|
||||||
CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}"
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: checker build tag
|
|
||||||
image: golang:1-alpine
|
|
||||||
commands:
|
|
||||||
- apk add --no-cache git make
|
|
||||||
- make
|
|
||||||
environment:
|
|
||||||
CHECKER_VERSION: "${DRONE_SEMVER}"
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: publish on Docker Hub
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
repo: happydomain/checker-email-keys
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
build_args:
|
|
||||||
- CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT}
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: publish on Docker Hub (tag)
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
repo: happydomain/checker-email-keys
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
build_args:
|
|
||||||
- CHECKER_VERSION=${DRONE_SEMVER}
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- cron
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: docker-manifest
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: publish on Docker Hub
|
|
||||||
image: plugins/manifest
|
|
||||||
settings:
|
|
||||||
auto_tag: true
|
|
||||||
ignore_missing: true
|
|
||||||
spec: .drone-manifest.yml
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
exclude:
|
|
||||||
- renovate/*
|
|
||||||
event:
|
|
||||||
- cron
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- build-amd64
|
|
||||||
- build-arm64
|
|
||||||
79
README.md
79
README.md
|
|
@ -36,40 +36,53 @@ keys it finds. It does **not** cryptographically verify them:
|
||||||
Treat a green report as "the record is well-formed and DNSSEC-signed",
|
Treat a green report as "the record is well-formed and DNSSEC-signed",
|
||||||
not as "the key is trustworthy".
|
not as "the key is trustworthy".
|
||||||
|
|
||||||
## Rules
|
## Tests run
|
||||||
|
|
||||||
| Code | Description | Severity |
|
All findings are tagged by severity (`info` / `warn` / `crit`) so the
|
||||||
|-----------------------------------|---------------------------------------------------------------------------------------------------|---------------------|
|
rule engine can fold them into a single `CheckState`.
|
||||||
| `dns_query_failed` | Verifies that the DNS lookup for the OPENPGPKEY/SMIMEA record succeeds. | CRITICAL |
|
|
||||||
| `dns_no_record` | Verifies that an OPENPGPKEY/SMIMEA record is published at the expected owner name. | CRITICAL |
|
### DNS (both record types)
|
||||||
| `dns_record_mismatch` | Verifies that the record returned by DNS matches the service-declared record. | WARNING |
|
|
||||||
| `dnssec_not_validated` | Verifies that the record is authenticated by DNSSEC (AD flag set). | CRITICAL |
|
| Code | Severity | What it catches |
|
||||||
| `owner_hash_mismatch` | Verifies that the owner-name first label equals hex(sha256(username))[:28]. | CRITICAL |
|
| --- | --- | --- |
|
||||||
| `pgp_parse_error` | Verifies that the OPENPGPKEY record decodes as a valid OpenPGP key. | CRITICAL |
|
| `dns_query_failed` | crit | The resolver returned an error or did not answer. |
|
||||||
| `pgp_primary_revoked` | Verifies that the OpenPGP primary key carries no revocation signature. | CRITICAL |
|
| `dns_no_record` | crit | The authoritative answer has no record at the expected owner. |
|
||||||
| `pgp_primary_expired` | Verifies that the OpenPGP primary key has not passed its self-signature expiry. | CRITICAL |
|
| `dnssec_not_validated` | crit / warn | The validating resolver did not set `AD`. RFC 7929/8162 mandate DNSSEC; the severity is configurable via `requireDNSSEC`. |
|
||||||
| `pgp_primary_expiring_soon` | Warns when the OpenPGP primary key expires within the configured window. | WARNING |
|
| `dns_record_mismatch` | warn | The record returned by DNS differs from the one declared in the service (typically a stale zone on the authoritative servers). |
|
||||||
| `pgp_weak_algorithm` | Verifies that OpenPGP keys do not use legacy algorithms (DSA/ElGamal). | WARNING |
|
| `owner_hash_mismatch` | crit | Record owner-name first label is not `sha256(localpart)[:28]`; mail clients will never find it. |
|
||||||
| `pgp_weak_key_size` | Verifies that OpenPGP RSA keys meet the minimum 2048-bit size (3072+ preferred). | CRITICAL |
|
|
||||||
| `pgp_no_encryption_subkey` | Verifies that at least one active OpenPGP key advertises encryption capability. | CRITICAL |
|
### OpenPGP-specific (RFC 7929)
|
||||||
| `pgp_no_identity` | Verifies that the OpenPGP key carries at least one self-signed User ID. | WARNING |
|
|
||||||
| `pgp_uid_mismatch` | Checks that at least one OpenPGP UID references <username@...>. | INFO |
|
| Code | Severity | What it catches |
|
||||||
| `pgp_multiple_entities` | Verifies that the record carries a single OpenPGP entity (RFC 7929). | WARNING |
|
| --- | --- | --- |
|
||||||
| `pgp_record_too_large` | Verifies that the OPENPGPKEY record stays below 4 KiB to fit typical UDP answers. | WARNING |
|
| `pgp_parse_error` | crit | Malformed base64 or OpenPGP packet stream. |
|
||||||
| `smimea_bad_usage` | Verifies that the SMIMEA usage field is 0, 1, 2, or 3. | CRITICAL |
|
| `pgp_no_entity` | crit | Record decoded but carries no valid entity. |
|
||||||
| `smimea_bad_selector` | Verifies that the SMIMEA selector field is 0 (Cert) or 1 (SPKI). | CRITICAL |
|
| `pgp_primary_revoked` | crit | Primary key has a revocation signature. |
|
||||||
| `smimea_bad_match_type` | Verifies that the SMIMEA matching type is 0 (Full), 1 (SHA-256), or 2 (SHA-512). | CRITICAL |
|
| `pgp_primary_expired` | crit | Self-signature expired; clients will refuse to encrypt. |
|
||||||
| `smimea_cert_parse_error` | Verifies that the SMIMEA record decodes as a valid X.509 certificate or SPKI. | CRITICAL |
|
| `pgp_primary_expiring_soon` | warn | Expires within the `certExpiryWarnDays` window (default 30). |
|
||||||
| `smimea_cert_not_yet_valid` | Verifies that the S/MIME certificate's NotBefore is in the past. | CRITICAL |
|
| `pgp_weak_algorithm` | warn | Uses DSA / ElGamal (phase-out). |
|
||||||
| `smimea_cert_expired` | Verifies that the S/MIME certificate's NotAfter is in the future. | CRITICAL |
|
| `pgp_weak_key_size` | crit / warn | RSA below 2048 bits is critical, 2048-3071 is a warn. |
|
||||||
| `smimea_cert_expiring_soon` | Warns when the S/MIME certificate expires within the configured window. | WARNING |
|
| `pgp_no_encryption_subkey` | crit | No active key in the entity advertises encryption capability. |
|
||||||
| `smimea_no_email_protection_eku` | Verifies that the S/MIME certificate advertises the emailProtection EKU. | CRITICAL |
|
| `pgp_no_identity` | warn | No self-signed User ID. |
|
||||||
| `smimea_missing_key_usage` | Verifies that the certificate carries digitalSignature and/or keyEncipherment key usage. | WARNING |
|
| `pgp_uid_mismatch` | info | None of the UIDs reference `<username@…>`. |
|
||||||
| `smimea_weak_signature_algorithm` | Verifies that the certificate is not signed with a deprecated algorithm (MD2/MD5/SHA-1). | CRITICAL |
|
| `pgp_multiple_entities` | warn | Record carries more than one entity (RFC 7929 recommends one). |
|
||||||
| `smimea_weak_key_size` | Verifies that SMIMEA RSA keys meet the minimum 2048-bit size (3072+ preferred). | CRITICAL |
|
| `pgp_record_too_large` | warn | Raw key > 4 KiB; forces UDP→TCP fallback on every lookup. |
|
||||||
| `smimea_self_signed` | Flags self-signed certificates paired with PKIX-EE (usage 1). | INFO |
|
|
||||||
| `smimea_email_mismatch` | Checks that at least one email SAN on the certificate begins with <username>@. | INFO |
|
### SMIMEA-specific (RFC 8162)
|
||||||
| `smimea_hash_only` | Notes that SMIMEA matching types 1/2 transport only a digest, preventing certificate inspection. | INFO |
|
|
||||||
|
| Code | Severity | What it catches |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `smimea_bad_usage` / `_selector` / `_match_type` | crit | Field outside the allowed range. |
|
||||||
|
| `smimea_cert_parse_error` | crit | Hex-encoded blob is not a valid X.509 certificate / SPKI. |
|
||||||
|
| `smimea_cert_expired` / `_not_yet_valid` | crit | `notBefore` / `notAfter` gate the current time out. |
|
||||||
|
| `smimea_cert_expiring_soon` | warn | Within the `certExpiryWarnDays` window. |
|
||||||
|
| `smimea_no_email_protection_eku` | crit / warn | Missing `emailProtection` EKU (RFC 8550/8551 agents will reject). |
|
||||||
|
| `smimea_missing_key_usage` | warn | Neither `digitalSignature` nor `keyEncipherment` key-usage is set. |
|
||||||
|
| `smimea_email_mismatch` | info | No email SAN starts with `<username>@`. |
|
||||||
|
| `smimea_weak_signature_algorithm` | crit | MD5 / SHA-1 based signature. |
|
||||||
|
| `smimea_weak_key_size` | crit / warn | RSA < 2048 / 3072 bits. |
|
||||||
|
| `smimea_self_signed` | info | Self-signed certificate paired with PKIX-EE usage. |
|
||||||
|
| `smimea_hash_only` | info | Matching-type 1/2 only carries a digest; certificate can't be inspected. |
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -193,13 +193,11 @@ func computeOwner(body serviceBody, prefix, parent string) (expected, recorded s
|
||||||
// Normalise: no double dots.
|
// Normalise: no double dots.
|
||||||
expected = strings.Replace(expected, "..", ".", -1)
|
expected = strings.Replace(expected, "..", ".", -1)
|
||||||
}
|
}
|
||||||
// happyDomain encodes service-embedded record owners relative to the
|
|
||||||
// parent zone, so we must join with parent before treating as FQDN.
|
|
||||||
switch {
|
switch {
|
||||||
case body.OpenPGP != nil && body.OpenPGP.Hdr.Name != "":
|
case body.OpenPGP != nil && body.OpenPGP.Hdr.Name != "":
|
||||||
recorded = dns.Fqdn(sdk.JoinRelative(body.OpenPGP.Hdr.Name, parent))
|
recorded = dns.Fqdn(body.OpenPGP.Hdr.Name)
|
||||||
case body.SMIMEA != nil && body.SMIMEA.Hdr.Name != "":
|
case body.SMIMEA != nil && body.SMIMEA.Hdr.Name != "":
|
||||||
recorded = dns.Fqdn(sdk.JoinRelative(body.SMIMEA.Hdr.Name, parent))
|
recorded = dns.Fqdn(body.SMIMEA.Hdr.Name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
go.mod
18
go.mod
|
|
@ -3,17 +3,17 @@ module git.happydns.org/checker-email-keys
|
||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.happydns.org/checker-sdk-go v1.7.0
|
git.happydns.org/checker-sdk-go v1.5.0
|
||||||
github.com/ProtonMail/go-crypto v1.4.1
|
github.com/ProtonMail/go-crypto v1.1.0-alpha.0
|
||||||
github.com/miekg/dns v1.1.72
|
github.com/miekg/dns v1.1.72
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cloudflare/circl v1.6.3 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
golang.org/x/crypto v0.51.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/mod v0.36.0 // indirect
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
golang.org/x/net v0.54.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.44.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/tools v0.45.0 // indirect
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
36
go.sum
36
go.sum
|
|
@ -1,22 +1,22 @@
|
||||||
git.happydns.org/checker-sdk-go v1.7.0 h1:dSgo2js5mfXluLc6x0WWZ0MQULd9XV2GI9z0Usk+Qgw=
|
git.happydns.org/checker-sdk-go v1.5.0 h1:5uD5Cm6xJ+lwnhbJ09iCXGHbYS9zRh+Yh0NeBHkAPBY=
|
||||||
git.happydns.org/checker-sdk-go v1.7.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI=
|
git.happydns.org/checker-sdk-go v1.5.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI=
|
||||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0=
|
||||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
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/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue