Compare commits

..

1 Commits

Author SHA1 Message Date
Xe Iaso 082a57a1a0 feat(logging): add option to hide log line source
This is almost certainly going to be a footgun, but it's not too
bad of a change to make. If users really do want to make getting
support more difficult, so be it.

Closes: #1537
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-03-24 15:33:23 +00:00
70 changed files with 352 additions and 932 deletions
-4
View File
@@ -34,7 +34,3 @@ de
resourced resourced
envoyproxy envoyproxy
unipromos unipromos
Samsung
wenet
qwertiko
setuplistener
+4 -2
View File
@@ -80,7 +80,6 @@ databento
dayjob dayjob
dco dco
DDOS DDOS
ddwrt
Debian Debian
debrpm debrpm
decaymap decaymap
@@ -103,7 +102,6 @@ duckduckbot
eerror eerror
ellenjoe ellenjoe
emacs emacs
embe
enbyware enbyware
etld etld
everyones everyones
@@ -121,6 +119,7 @@ FCr
fcrdns fcrdns
fediverse fediverse
ffprobe ffprobe
FFXIV
fhdr fhdr
financials financials
finfos finfos
@@ -222,6 +221,7 @@ LLU
loadbalancer loadbalancer
lol lol
lominsa lominsa
maintainership
malware malware
mcr mcr
memes memes
@@ -239,6 +239,7 @@ mymaster
mypass mypass
myuser myuser
nbf nbf
Necron
nepeat nepeat
netsurf netsurf
nginx nginx
@@ -333,6 +334,7 @@ spyderbot
srcip srcip
srv srv
stackoverflow stackoverflow
Stargate
startprecmd startprecmd
stoppostcmd stoppostcmd
storetest storetest
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "1.25.7" go-version: "1.25.7"
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
+2 -2
View File
@@ -39,14 +39,14 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
- name: Log into registry - name: Log into registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Log into registry - name: Log into registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with: with:
registry: ghcr.io registry: ghcr.io
username: techarohq username: techarohq
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
+2 -2
View File
@@ -27,12 +27,12 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
- name: Cache playwright binaries - name: Cache playwright binaries
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
id: playwright-cache id: playwright-cache
with: with:
path: | path: |
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
@@ -29,7 +29,7 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
steps: steps:
- name: check-spelling - name: check-spelling
id: spelling id: spelling
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26 uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
with: with:
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
checkout: true checkout: true
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Log into registry - name: Log into registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
+2 -2
View File
@@ -30,13 +30,13 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Install CI target SSH key - name: Install CI target SSH key
uses: shimataro/ssh-key-action@87a8f067114a8ce263df83e9ed5c849953548bc3 # v2.8.1 uses: shimataro/ssh-key-action@6b84f2e793b32fa0b03a379cadadec75cc539391 # v2.8.0
with: with:
key: ${{ secrets.CI_SSH_KEY }} key: ${{ secrets.CI_SSH_KEY }}
name: id_rsa name: id_rsa
known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }} known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version: "stable" go-version: "stable"
+2 -2
View File
@@ -21,7 +21,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
- name: Run zizmor 🌈 - name: Run zizmor 🌈
run: uvx zizmor --format sarif . > results.sarif run: uvx zizmor --format sarif . > results.sarif
@@ -29,7 +29,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with: with:
sarif_file: results.sarif sarif_file: results.sarif
category: zizmor category: zizmor
-9
View File
@@ -73,15 +73,6 @@ Anubis is brought to you by sponsors and donors like:
<a href="https://www.anexia.com/"> <a href="https://www.anexia.com/">
<img src="./docs/static/img/sponsors/anexia-cloudsolutions-logo.webp" alt="ANEXIA Cloud Solutions" height="64"> <img src="./docs/static/img/sponsors/anexia-cloudsolutions-logo.webp" alt="ANEXIA Cloud Solutions" height="64">
</a> </a>
<a href="https://dd-wrt.com/">
<img src="./docs/static/img/sponsors/ddwrt-logo.webp" alt="embeDD GmbH" height="64">
</a>
<a href="https://www.qwertiko.de?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/qwertiko-logo.webp" alt="Qwertiko" height="64">
</a>
<a href="https://wenet.pl/?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/wenet-logo.webp" alt="Wenet" height="64">
</a>
## Overview ## Overview
+131 -27
View File
@@ -17,10 +17,12 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/http/pprof"
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
@@ -31,12 +33,12 @@ import (
"github.com/TecharoHQ/anubis/internal" "github.com/TecharoHQ/anubis/internal"
libanubis "github.com/TecharoHQ/anubis/lib" libanubis "github.com/TecharoHQ/anubis/lib"
"github.com/TecharoHQ/anubis/lib/config" "github.com/TecharoHQ/anubis/lib/config"
"github.com/TecharoHQ/anubis/lib/metrics"
botPolicy "github.com/TecharoHQ/anubis/lib/policy" botPolicy "github.com/TecharoHQ/anubis/lib/policy"
"github.com/TecharoHQ/anubis/lib/thoth" "github.com/TecharoHQ/anubis/lib/thoth"
"github.com/TecharoHQ/anubis/web" "github.com/TecharoHQ/anubis/web"
"github.com/facebookgo/flagenv" "github.com/facebookgo/flagenv"
_ "github.com/joho/godotenv/autoload" _ "github.com/joho/godotenv/autoload"
"github.com/prometheus/client_golang/prometheus/promhttp"
healthv1 "google.golang.org/grpc/health/grpc_health_v1" healthv1 "google.golang.org/grpc/health/grpc_health_v1"
) )
@@ -117,6 +119,33 @@ func doHealthCheck() error {
return nil return nil
} }
// parseBindNetFromAddr determine bind network and address based on the given network and address.
func parseBindNetFromAddr(address string) (string, string) {
defaultScheme := "http://"
if !strings.Contains(address, "://") {
if strings.HasPrefix(address, ":") {
address = defaultScheme + "localhost" + address
} else {
address = defaultScheme + address
}
}
bindUri, err := url.Parse(address)
if err != nil {
log.Fatal(fmt.Errorf("failed to parse bind URL: %w", err))
}
switch bindUri.Scheme {
case "unix":
return "unix", bindUri.Path
case "tcp", "http", "https":
return "tcp", bindUri.Host
default:
log.Fatal(fmt.Errorf("unsupported network scheme %s in address %s", bindUri.Scheme, address))
}
return "", address
}
func parseSameSite(s string) http.SameSite { func parseSameSite(s string) http.SameSite {
switch strings.ToLower(s) { switch strings.ToLower(s) {
case "none": case "none":
@@ -133,6 +162,53 @@ func parseSameSite(s string) http.SameSite {
return http.SameSiteDefaultMode return http.SameSiteDefaultMode
} }
func setupListener(network string, address string) (net.Listener, string) {
formattedAddress := ""
if network == "" {
// keep compatibility
network, address = parseBindNetFromAddr(address)
}
switch network {
case "unix":
formattedAddress = "unix:" + address
case "tcp":
if strings.HasPrefix(address, ":") { // assume it's just a port e.g. :4259
formattedAddress = "http://localhost" + address
} else {
formattedAddress = "http://" + address
}
default:
formattedAddress = fmt.Sprintf(`(%s) %s`, network, address)
}
listener, err := net.Listen(network, address)
if err != nil {
log.Fatal(fmt.Errorf("failed to bind to %s: %w", formattedAddress, err))
}
// additional permission handling for unix sockets
if network == "unix" {
mode, err := strconv.ParseUint(*socketMode, 8, 0)
if err != nil {
listener.Close()
log.Fatal(fmt.Errorf("could not parse socket mode %s: %w", *socketMode, err))
}
err = os.Chmod(address, os.FileMode(mode))
if err != nil {
err := listener.Close()
if err != nil {
log.Printf("failed to close listener: %v", err)
}
log.Fatal(fmt.Errorf("could not change socket mode: %w", err))
}
}
return listener, formattedAddress
}
func makeReverseProxy(target string, targetSNI string, targetHost string, insecureSkipVerify bool, targetDisableKeepAlive bool) (http.Handler, error) { func makeReverseProxy(target string, targetSNI string, targetHost string, insecureSkipVerify bool, targetDisableKeepAlive bool) (http.Handler, error) {
targetUri, err := url.Parse(target) targetUri, err := url.Parse(target)
if err != nil { if err != nil {
@@ -200,7 +276,7 @@ func main() {
internal.SetHealth("anubis", healthv1.HealthCheckResponse_NOT_SERVING) internal.SetHealth("anubis", healthv1.HealthCheckResponse_NOT_SERVING)
lg := internal.InitSlog(*slogLevel, os.Stderr) lg := internal.InitSlog(*slogLevel, os.Stderr, false)
lg.Info("starting up Anubis") lg.Info("starting up Anubis")
if *healthcheck { if *healthcheck {
@@ -228,6 +304,11 @@ func main() {
wg := new(sync.WaitGroup) wg := new(sync.WaitGroup)
if *metricsBind != "" {
wg.Add(1)
go metricsServer(ctx, *lg.With("subsystem", "metrics"), wg.Done)
}
var rp http.Handler var rp http.Handler
// when using anubis via Systemd and environment variables, then it is not possible to set targe to an empty string but only to space // when using anubis via Systemd and environment variables, then it is not possible to set targe to an empty string but only to space
if strings.TrimSpace(*target) != "" { if strings.TrimSpace(*target) != "" {
@@ -267,26 +348,6 @@ func main() {
lg.Debug("swapped to new logger") lg.Debug("swapped to new logger")
slog.SetDefault(lg) slog.SetDefault(lg)
if *metricsBind != "" || policy.Metrics != nil {
wg.Add(1)
ms := &metrics.Server{
Config: policy.Metrics,
Log: lg,
}
if policy.Metrics == nil {
lg.Debug("migrating flags to metrics config", "bind", *metricsBind, "network", *metricsBindNetwork, "socket-mode", *socketMode)
ms.Config = &config.Metrics{
Bind: *metricsBind,
Network: *metricsBindNetwork,
SocketMode: *socketMode,
}
}
go ms.Run(ctx, wg.Done)
}
// Warn if persistent storage is used without a configured signing key // Warn if persistent storage is used without a configured signing key
if policy.Store.IsPersistent() { if policy.Store.IsPersistent() {
if *hs512Secret == "" && *ed25519PrivateKeyHex == "" && *ed25519PrivateKeyHexFile == "" { if *hs512Secret == "" && *ed25519PrivateKeyHex == "" && *ed25519PrivateKeyHexFile == "" {
@@ -423,11 +484,7 @@ func main() {
h = internal.JA4H(h) h = internal.JA4H(h)
srv := http.Server{Handler: h, ErrorLog: internal.GetFilteredHTTPLogger()} srv := http.Server{Handler: h, ErrorLog: internal.GetFilteredHTTPLogger()}
listener, listenerUrl, err := internal.SetupListener(*bindNetwork, *bind, *socketMode) listener, listenerUrl := setupListener(*bindNetwork, *bind)
if err != nil {
log.Fatalf("SetupListener(%q, %q, %q): %v", *bindNetwork, *bind, *socketMode, err)
}
lg.Info( lg.Info(
"listening", "listening",
"url", listenerUrl, "url", listenerUrl,
@@ -462,6 +519,53 @@ func main() {
wg.Wait() wg.Wait()
} }
func metricsServer(ctx context.Context, lg slog.Logger, done func()) {
defer done()
mux := http.NewServeMux()
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
st, ok := internal.GetHealth("anubis")
if !ok {
slog.Error("health service anubis does not exist, file a bug")
}
switch st {
case healthv1.HealthCheckResponse_NOT_SERVING:
http.Error(w, "NOT OK", http.StatusInternalServerError)
return
case healthv1.HealthCheckResponse_SERVING:
fmt.Fprintln(w, "OK")
return
default:
http.Error(w, "UNKNOWN", http.StatusFailedDependency)
return
}
})
srv := http.Server{Handler: mux, ErrorLog: internal.GetFilteredHTTPLogger()}
listener, metricsUrl := setupListener(*metricsBindNetwork, *metricsBind)
lg.Debug("listening for metrics", "url", metricsUrl)
go func() {
<-ctx.Done()
c, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(c); err != nil {
log.Printf("cannot shut down: %v", err)
}
}()
if err := srv.Serve(listener); !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}
func extractEmbedFS(fsys embed.FS, root string, destDir string) error { func extractEmbedFS(fsys embed.FS, root string, destDir string) error {
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
if err != nil { if err != nil {
+5 -5
View File
@@ -28,7 +28,7 @@ func main() {
flagenv.Parse() flagenv.Parse()
flag.Parse() flag.Parse()
slog.SetDefault(internal.InitSlog(*slogLevel, os.Stderr)) slog.SetDefault(internal.InitSlog(*slogLevel, os.Stderr, false))
koDockerRepo := strings.TrimSuffix(*dockerRepo, "/"+filepath.Base(*dockerRepo)) koDockerRepo := strings.TrimSuffix(*dockerRepo, "/"+filepath.Base(*dockerRepo))
@@ -159,8 +159,8 @@ func run(command string) (string, error) {
} }
func setOutput(key, val string) { func setOutput(key, val string) {
github_output := os.Getenv("GITHUB_OUTPUT") github_output := os.Getenv("GITHUB_OUTPUT")
f, _ := os.OpenFile(github_output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) f, _ := os.OpenFile(github_output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
fmt.Fprintf(f, "%s=%s\n", key, val) fmt.Fprintf(f, "%s=%s\n", key, val)
f.Close() f.Close()
} }
-9
View File
@@ -166,15 +166,6 @@ status_codes:
CHALLENGE: 200 CHALLENGE: 200
DENY: 200 DENY: 200
# # Configuration for the metrics server. See the docs for more information:
# #
# # https://anubis.techaro.lol/docs/admin/policies#metrics-server
# #
# # This is commented out by default so that command line flags take precedence.
# metrics:
# bind: ":9090"
# network: "tcp"
# Anubis can store temporary data in one of a few backends. See the storage # Anubis can store temporary data in one of a few backends. See the storage
# backends section of the docs for more information: # backends section of the docs for more information:
# #
+4 -3
View File
@@ -11,15 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
<!-- This changes the project to: -->
- Move metrics server configuration to [the policy file](./admin/policies.mdx#metrics-server).
- Expose [pprof endpoints](https://pkg.go.dev/net/http/pprof) on the metrics listener to enable profiling Anubis in production. - Expose [pprof endpoints](https://pkg.go.dev/net/http/pprof) on the metrics listener to enable profiling Anubis in production.
- fix: prevent nil pointer panic in challenge validation when threshold rules match during PassChallenge (#1463) - fix: prevent nil pointer panic in challenge validation when threshold rules match during PassChallenge (#1463)
- Instruct reverse proxies to not cache error pages. - Instruct reverse proxies to not cache error pages.
- Fixed mixed tab/space indentation in Caddy documentation code block - Fixed mixed tab/space indentation in Caddy documentation code block
- Improve error messages and fix broken REDIRECT_DOMAINS link in docs ([#1193](https://github.com/TecharoHQ/anubis/issues/1193)) - Improve error messages and fix broken REDIRECT_DOMAINS link in docs ([#1193](https://github.com/TecharoHQ/anubis/issues/1193))
- Add Bulgarian locale ([#1394](https://github.com/TecharoHQ/anubis/pull/1394)) - Add Bulgarian locale ([#1394](https://github.com/TecharoHQ/anubis/pull/1394))
- Add option to hide source code origin of log lines in [logging configuration](./admin/policies.mdx#logging-management)
<!-- This changes the project to: -->
- Fix CEL internal errors when iterating `headers`/`query` map wrappers by implementing map iterators for `HTTPHeaders` and `URLValues` ([#1465](https://github.com/TecharoHQ/anubis/pull/1465)). - Fix CEL internal errors when iterating `headers`/`query` map wrappers by implementing map iterators for `HTTPHeaders` and `URLValues` ([#1465](https://github.com/TecharoHQ/anubis/pull/1465)).
## v1.25.0: Necron ## v1.25.0: Necron
+3 -3
View File
@@ -87,15 +87,15 @@ Anubis uses these environment variables for configuration:
| `ED25519_PRIVATE_KEY_HEX_FILE` | unset | Path to a file containing the hex-encoded ed25519 private key. Only one of this or its sister option may be set. **Required when using persistent storage backends** (like bbolt) to ensure challenges survive service restarts. When running multiple instances on the same base domain, the key must be the same across all instances. | | `ED25519_PRIVATE_KEY_HEX_FILE` | unset | Path to a file containing the hex-encoded ed25519 private key. Only one of this or its sister option may be set. **Required when using persistent storage backends** (like bbolt) to ensure challenges survive service restarts. When running multiple instances on the same base domain, the key must be the same across all instances. |
| `ERROR_TITLE` | unset | <EO /> If set, override the translation stack to show a custom title for error pages such as "Something went wrong!". See [Customizing messages](./botstopper.mdx#customizing-messages) for more details. | | `ERROR_TITLE` | unset | <EO /> If set, override the translation stack to show a custom title for error pages such as "Something went wrong!". See [Customizing messages](./botstopper.mdx#customizing-messages) for more details. |
| `JWT_RESTRICTION_HEADER` | `X-Real-IP` | If set, the JWT is only valid if the current value of this header matches the value when the JWT was created. You can use it e.g. to restrict a JWT to the source IP of the user using `X-Real-IP`. | | `JWT_RESTRICTION_HEADER` | `X-Real-IP` | If set, the JWT is only valid if the current value of this header matches the value when the JWT was created. You can use it e.g. to restrict a JWT to the source IP of the user using `X-Real-IP`. |
| `METRICS_BIND` | `:9090` | The legacy configuration value for the network address that Anubis serves Prometheus metrics on. Please migrate this to [the policy file](./policies.mdx#metrics-server) as soon as possible. | | `METRICS_BIND` | `:9090` | The network address that Anubis serves Prometheus metrics on. See `BIND` for more information. |
| `METRICS_BIND_NETWORK` | `tcp` | The legacy configuration value for the address family that Anubis serves Prometheus metrics on. Please migrate this to [the policy file](./policies.mdx#metrics-server) as soon as possible. | | `METRICS_BIND_NETWORK` | `tcp` | The address family that the Anubis metrics server listens on. See `BIND_NETWORK` for more information. |
| `OG_EXPIRY_TIME` | `24h` | The expiration time for the Open Graph tag cache. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. | | `OG_EXPIRY_TIME` | `24h` | The expiration time for the Open Graph tag cache. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. |
| `OG_PASSTHROUGH` | `false` | If set to `true`, Anubis will enable Open Graph tag passthrough. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. | | `OG_PASSTHROUGH` | `false` | If set to `true`, Anubis will enable Open Graph tag passthrough. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. |
| `OG_CACHE_CONSIDER_HOST` | `false` | If set to `true`, Anubis will consider the host in the Open Graph tag cache key. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. | | `OG_CACHE_CONSIDER_HOST` | `false` | If set to `true`, Anubis will consider the host in the Open Graph tag cache key. Prefer using [the policy file](./configuration/open-graph.mdx) to configure the Open Graph subsystem. |
| `OVERLAY_FOLDER` | unset | <EO /> If set, treat the given path as an [overlay folder](./botstopper.mdx#custom-images-and-css), allowing you to customize CSS, fonts, images, and add other assets to BotStopper deployments. | | `OVERLAY_FOLDER` | unset | <EO /> If set, treat the given path as an [overlay folder](./botstopper.mdx#custom-images-and-css), allowing you to customize CSS, fonts, images, and add other assets to BotStopper deployments. |
| `POLICY_FNAME` | unset | The file containing [bot policy configuration](./policies.mdx). See the bot policy documentation for more details. If unset, the default bot policy configuration is used. | | `POLICY_FNAME` | unset | The file containing [bot policy configuration](./policies.mdx). See the bot policy documentation for more details. If unset, the default bot policy configuration is used. |
| `PUBLIC_URL` | unset | The externally accessible URL for this Anubis instance, used for constructing redirect URLs (e.g., for Traefik forwardAuth). Leave it unset when Anubis terminates traffic directly (sidecar/standalone deployments) or redirect building will fail with `redir=null`. | | `PUBLIC_URL` | unset | The externally accessible URL for this Anubis instance, used for constructing redirect URLs (e.g., for Traefik forwardAuth). Leave it unset when Anubis terminates traffic directly (sidecar/standalone deployments) or redirect building will fail with `redir=null`. |
| `REDIRECT_DOMAINS` | unset | Comma-separated list of domain names that Anubis should allow redirects to when passing a challenge. See [Redirect Domain Configuration](./configuration/redirect-domains.mdx) for more details. | | `REDIRECT_DOMAINS` | unset | Comma-separated list of domain names that Anubis should allow redirects to when passing a challenge. See [Redirect Domain Configuration](./configuration/redirect-domains.mdx) for more details. |
| `SERVE_ROBOTS_TXT` | `false` | If set `true`, Anubis will serve a default `robots.txt` file that disallows all known AI scrapers by name and then additionally disallows every scraper. This is useful if facts and circumstances make it difficult to change the underlying service to serve such a `robots.txt` file. | | `SERVE_ROBOTS_TXT` | `false` | If set `true`, Anubis will serve a default `robots.txt` file that disallows all known AI scrapers by name and then additionally disallows every scraper. This is useful if facts and circumstances make it difficult to change the underlying service to serve such a `robots.txt` file. |
| `SLOG_LEVEL` | `INFO` | The log level for structured logging. Valid values are `DEBUG`, `INFO`, `WARN`, and `ERROR`. Set to `DEBUG` to see all requests, evaluations, and detailed diagnostic information. | | `SLOG_LEVEL` | `INFO` | The log level for structured logging. Valid values are `DEBUG`, `INFO`, `WARN`, and `ERROR`. Set to `DEBUG` to see all requests, evaluations, and detailed diagnostic information. |
| `SOCKET_MODE` | `0770` | _Only used when at least one of the `*_BIND_NETWORK` variables are set to `unix`._ The socket mode (permissions) for Unix domain sockets. | | `SOCKET_MODE` | `0770` | _Only used when at least one of the `*_BIND_NETWORK` variables are set to `unix`._ The socket mode (permissions) for Unix domain sockets. |
+1 -21
View File
@@ -117,27 +117,6 @@ remote_addresses:
- 100.64.0.0/10 - 100.64.0.0/10
``` ```
## Metrics server
Anubis includes support for [Prometheus-style metrics](https://prometheus.io/docs/introduction/overview/), allowing systems administrators to monitor Anubis' performance and effectiveness. This is a separate HTTP server with metrics, health checking, and debug routes.
Anubis' metrics server is configured with the `metrics` block in the configuration file:
```yaml
metrics:
bind: ":9090"
network: "tcp"
```
If you want to bind metrics to a Unix socket, make sure to set the network to `unix` and add a socket mode:
```yaml
metrics:
bind: "/tmp/anubis_metrics.sock"
network: unix
socketMode: "0700" # must be a string
```
## Imprint / Impressum support ## Imprint / Impressum support
Anubis has support for showing imprint / impressum information. This is defined in the `impressum` block of your configuration. See [Imprint / Impressum configuration](./configuration/impressum.mdx) for more information. Anubis has support for showing imprint / impressum information. This is defined in the `impressum` block of your configuration. See [Imprint / Impressum configuration](./configuration/impressum.mdx) for more information.
@@ -360,6 +339,7 @@ Anubis exposes the following logging settings in the policy file:
| `level` | [log level](#log-levels) | `info` | The logging level threshold. Any logs that are at or above this threshold will be drained to the sink. Any other logs will be discarded. | | `level` | [log level](#log-levels) | `info` | The logging level threshold. Any logs that are at or above this threshold will be drained to the sink. Any other logs will be discarded. |
| `sink` | string | `stdio`, `file` | The sink where the logs drain to as they are being recorded in Anubis. | | `sink` | string | `stdio`, `file` | The sink where the logs drain to as they are being recorded in Anubis. |
| `parameters` | object | | Parameters for the given logging sink. This will vary based on the logging sink of choice. See below for more information. | | `parameters` | object | | Parameters for the given logging sink. This will vary based on the logging sink of choice. See below for more information. |
| `hideSource` | boolean | `false` | If set, hide the details of which line of code triggered a log line. Setting this to `true` may make getting support more difficult. |
Anubis supports the following logging sinks: Anubis supports the following logging sinks:
-9
View File
@@ -87,15 +87,6 @@ Anubis is brought to you by sponsors and donors like:
height="64" height="64"
/> />
</a> </a>
<a href="https://dd-wrt.com/">
<img src="/img/sponsors/ddwrt-logo.webp" alt="embeDD GmbH" height="64" />
</a>
<a href="https://www.qwertiko.de?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="/img/sponsors/qwertiko-logo.webp" alt="Qwertiko" height="64" />
</a>
<a href="https://wenet.pl/?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="/img/sponsors/wenet-logo.webp" alt="Wenet" height="64" />
</a>
## Overview ## Overview
@@ -32,14 +32,3 @@ This interpreter is much slower than native code because it has to translate eac
Some users choose to disable JIT as a hardening measure against theoretical browser exploits. This is a reasonable choice if you face targeted attacks from well-resourced adversaries (such as nation-state actors), but it comes with real performance costs. Some users choose to disable JIT as a hardening measure against theoretical browser exploits. This is a reasonable choice if you face targeted attacks from well-resourced adversaries (such as nation-state actors), but it comes with real performance costs.
If you've disabled JIT and find Anubis checks slow, re-enabling JIT is the fix. There is no way for Anubis to work around this on our end. If you've disabled JIT and find Anubis checks slow, re-enabling JIT is the fix. There is no way for Anubis to work around this on our end.
## What versions of browsers does Anubis support?
Anubis is written mainly by a single person in a basement in Canada. As such it is impossible for Anubis to support every version of every browser on the planet. As such, here's a few rules of thumb for the browsers that Anubis focuses on supporting:
- At least the two (2) most recent LTS releases of Firefox and Chrome.
- At least the version of Chromium as used by the Samsung Browser on Android.
- At least the last version of Chromium and Firefox that are known to run on Windows 7.
- At least the version of Safari that runs on the second-to-oldest iPhone model currently on the market.
We cannot give more cohesive version bounds than this. If you run into problems, please file an issue. Sometimes you may just need to upgrade hardware though.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

+9 -3
View File
@@ -10,7 +10,7 @@ import (
"strings" "strings"
) )
func InitSlog(level string, sink io.Writer) *slog.Logger { func InitSlog(level string, sink io.Writer, hideSource bool) *slog.Logger {
var programLevel slog.Level var programLevel slog.Level
if err := (&programLevel).UnmarshalText([]byte(level)); err != nil { if err := (&programLevel).UnmarshalText([]byte(level)); err != nil {
fmt.Fprintf(os.Stderr, "invalid log level %s: %v, using info\n", level, err) fmt.Fprintf(os.Stderr, "invalid log level %s: %v, using info\n", level, err)
@@ -20,10 +20,16 @@ func InitSlog(level string, sink io.Writer) *slog.Logger {
leveler := &slog.LevelVar{} leveler := &slog.LevelVar{}
leveler.Set(programLevel) leveler.Set(programLevel)
h := slog.NewJSONHandler(sink, &slog.HandlerOptions{ opts := &slog.HandlerOptions{
AddSource: true, AddSource: true,
Level: leveler, Level: leveler,
}) }
if hideSource {
opts.AddSource = false
}
h := slog.NewJSONHandler(sink, opts)
result := slog.New(h) result := slog.New(h)
return result return result
} }
-92
View File
@@ -1,92 +0,0 @@
package internal
import (
"errors"
"fmt"
"net"
"net/url"
"os"
"strconv"
"strings"
)
// parseBindNetFromAddr determine bind network and address based on the given network and address.
func parseBindNetFromAddr(address string) (string, string, error) {
defaultScheme := "http://"
if !strings.Contains(address, "://") {
if strings.HasPrefix(address, ":") {
address = defaultScheme + "localhost" + address
} else {
address = defaultScheme + address
}
}
bindUri, err := url.Parse(address)
if err != nil {
return "", "", fmt.Errorf("failed to parse bind URL: %w", err)
}
switch bindUri.Scheme {
case "unix":
return "unix", bindUri.Path, nil
case "tcp", "http", "https":
return "tcp", bindUri.Host, nil
default:
return "", "", fmt.Errorf("unsupported network scheme %s in address %s", bindUri.Scheme, address)
}
}
// SetupListener sets up a network listener based on the input from configuration
// envvars. It returns a network listener and the URL to that listener or an error.
func SetupListener(network, address, socketMode string) (net.Listener, string, error) {
formattedAddress := ""
var err error
if network == "" {
// keep compatibility
network, address, err = parseBindNetFromAddr(address)
}
if err != nil {
return nil, "", fmt.Errorf("can't parse bind and network: %w", err)
}
switch network {
case "unix":
formattedAddress = "unix:" + address
case "tcp":
if strings.HasPrefix(address, ":") { // assume it's just a port e.g. :4259
formattedAddress = "http://localhost" + address
} else {
formattedAddress = "http://" + address
}
default:
formattedAddress = fmt.Sprintf(`(%s) %s`, network, address)
}
ln, err := net.Listen(network, address)
if err != nil {
return nil, "", fmt.Errorf("failed to bind to %s: %w", formattedAddress, err)
}
// additional permission handling for unix sockets
if network == "unix" {
mode, err := strconv.ParseUint(socketMode, 8, 0)
if err != nil {
ln.Close()
return nil, "", fmt.Errorf("could not parse socket mode %s: %w", socketMode, err)
}
err = os.Chmod(address, os.FileMode(mode))
if err != nil {
err := fmt.Errorf("could not change socket mode: %w", err)
clErr := ln.Close()
if clErr != nil {
return nil, "", errors.Join(err, clErr)
}
return nil, "", err
}
}
return ln, formattedAddress, nil
}
-180
View File
@@ -1,180 +0,0 @@
package internal
import (
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
)
func TestParseBindNetFromAddr(t *testing.T) {
for _, tt := range []struct {
name string
address string
wantErr bool
network string
bind string
}{
{
name: "simple tcp",
address: "localhost:9090",
wantErr: false,
network: "tcp",
bind: "localhost:9090",
},
{
name: "simple unix",
address: "unix:///tmp/foo.sock",
wantErr: false,
network: "unix",
bind: "/tmp/foo.sock",
},
{
name: "invalid network",
address: "foo:///tmp/bar.sock",
wantErr: true,
},
{
name: "tcp uri",
address: "tcp://[::]:9090",
wantErr: false,
network: "tcp",
bind: "[::]:9090",
},
{
name: "http uri",
address: "http://[::]:9090",
wantErr: false,
network: "tcp",
bind: "[::]:9090",
},
{
name: "https uri",
address: "https://[::]:9090",
wantErr: false,
network: "tcp",
bind: "[::]:9090",
},
} {
t.Run(tt.name, func(t *testing.T) {
network, bind, err := parseBindNetFromAddr(tt.address)
switch {
case tt.wantErr && err == nil:
t.Errorf("parseBindNetFromAddr(%q) should have errored but did not", tt.address)
case !tt.wantErr && err != nil:
t.Errorf("parseBindNetFromAddr(%q) threw an error: %v", tt.address, err)
}
if network != tt.network {
t.Errorf("parseBindNetFromAddr(%q) wanted network: %q, got: %q", tt.address, tt.network, network)
}
if bind != tt.bind {
t.Errorf("parseBindNetFromAddr(%q) wanted bind: %q, got: %q", tt.address, tt.bind, bind)
}
})
}
}
func TestSetupListener(t *testing.T) {
td := t.TempDir()
for _, tt := range []struct {
name string
network, address, socketMode string
wantErr bool
socketURLPrefix string
}{
{
name: "simple tcp",
network: "",
address: ":0",
wantErr: false,
socketURLPrefix: "http://localhost:",
},
{
name: "simple unix",
network: "",
address: "unix://" + filepath.Join(td, "a"),
socketMode: "0770",
wantErr: false,
socketURLPrefix: "unix:" + filepath.Join(td, "a"),
},
{
name: "tcp",
network: "tcp",
address: ":0",
wantErr: false,
socketURLPrefix: "http://localhost:",
},
{
name: "udp",
network: "udp",
address: ":0",
wantErr: true,
socketURLPrefix: "http://localhost:",
},
{
name: "unix socket",
network: "unix",
socketMode: "0770",
address: filepath.Join(td, "a"),
wantErr: false,
socketURLPrefix: "unix:" + filepath.Join(td, "a"),
},
{
name: "invalid socket mode",
network: "unix",
socketMode: "taco bell",
address: filepath.Join(td, "a"),
wantErr: true,
socketURLPrefix: "unix:" + filepath.Join(td, "a"),
},
{
name: "empty socket mode",
network: "unix",
socketMode: "",
address: filepath.Join(td, "a"),
wantErr: true,
socketURLPrefix: "unix:" + filepath.Join(td, "a"),
},
} {
t.Run(tt.name, func(t *testing.T) {
ln, socketURL, err := SetupListener(tt.network, tt.address, tt.socketMode)
switch {
case tt.wantErr && err == nil:
t.Errorf("SetupListener(%q, %q, %q) should have errored but did not", tt.network, tt.address, tt.socketMode)
case !tt.wantErr && err != nil:
t.Fatalf("SetupListener(%q, %q, %q) threw an error: %v", tt.network, tt.address, tt.socketMode, err)
}
if ln != nil {
defer ln.Close()
}
if !tt.wantErr && !strings.HasPrefix(socketURL, tt.socketURLPrefix) {
t.Errorf("SetupListener(%q, %q, %q) should have returned a URL with prefix %q but got: %q", tt.network, tt.address, tt.socketMode, tt.socketURLPrefix, socketURL)
}
if tt.socketMode != "" {
mode, err := strconv.ParseUint(tt.socketMode, 8, 0)
if err != nil {
return
}
sockPath := strings.TrimPrefix(socketURL, "unix:")
st, err := os.Stat(sockPath)
if err != nil {
t.Fatalf("can't os.Stat(%q): %v", sockPath, err)
}
if st.Mode().Perm() != fs.FileMode(mode) {
t.Errorf("file mode of %q should be %s but is actually %s", sockPath, strconv.FormatUint(mode, 8), strconv.FormatUint(uint64(st.Mode()), 8))
}
}
})
}
}
-9
View File
@@ -334,7 +334,6 @@ type fileConfig struct {
DNSBL bool `json:"dnsbl"` DNSBL bool `json:"dnsbl"`
DNSTTL DnsTTL `json:"dns_ttl"` DNSTTL DnsTTL `json:"dns_ttl"`
Logging *Logging `json:"logging"` Logging *Logging `json:"logging"`
Metrics *Metrics `json:"metrics,omitempty"`
} }
func (c *fileConfig) Valid() error { func (c *fileConfig) Valid() error {
@@ -376,12 +375,6 @@ func (c *fileConfig) Valid() error {
} }
} }
if c.Metrics != nil {
if err := c.Metrics.Valid(); err != nil {
errs = append(errs, err)
}
}
if len(errs) != 0 { if len(errs) != 0 {
return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...)) return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...))
} }
@@ -424,7 +417,6 @@ func Load(fin io.Reader, fname string) (*Config, error) {
StatusCodes: c.StatusCodes, StatusCodes: c.StatusCodes,
Store: c.Store, Store: c.Store,
Logging: c.Logging, Logging: c.Logging,
Metrics: c.Metrics,
} }
if c.OpenGraph.TimeToLive != "" { if c.OpenGraph.TimeToLive != "" {
@@ -516,7 +508,6 @@ type Config struct {
Logging *Logging Logging *Logging
DNSBL bool DNSBL bool
DNSTTL DnsTTL DNSTTL DnsTTL
Metrics *Metrics
} }
func (c Config) Valid() error { func (c Config) Valid() error {
+1
View File
@@ -17,6 +17,7 @@ type Logging struct {
Sink string `json:"sink"` // Logging sink, either "stdio" or "file" Sink string `json:"sink"` // Logging sink, either "stdio" or "file"
Level *slog.Level `json:"level"` // Log level, if set supersedes the level in flags Level *slog.Level `json:"level"` // Log level, if set supersedes the level in flags
Parameters *LoggingFileConfig `json:"parameters"` // Logging parameters, to be dynamic in the future Parameters *LoggingFileConfig `json:"parameters"` // Logging parameters, to be dynamic in the future
HideSource bool `json:"hideSource"` // If true, hide the `source` field in log lines
} }
const ( const (
-54
View File
@@ -1,54 +0,0 @@
package config
import (
"errors"
"fmt"
"strconv"
)
var (
ErrInvalidMetricsConfig = errors.New("config: invalid metrics configuration")
ErrNoMetricsBind = errors.New("config.Metrics: must define bind")
ErrNoMetricsNetwork = errors.New("config.Metrics: must define network")
ErrNoMetricsSocketMode = errors.New("config.Metrics: must define socket mode when using unix sockets")
ErrInvalidMetricsSocketMode = errors.New("config.Metrics: invalid unix socket mode")
ErrInvalidMetricsNetwork = errors.New("config.Metrics: invalid metrics network")
)
type Metrics struct {
Bind string `json:"bind" yaml:"bind"`
Network string `json:"network" yaml:"network"`
SocketMode string `json:"socketMode" yaml:"socketMode"`
}
func (m *Metrics) Valid() error {
var errs []error
if m.Bind == "" {
errs = append(errs, ErrNoMetricsBind)
}
if m.Network == "" {
errs = append(errs, ErrNoMetricsNetwork)
}
switch m.Network {
case "tcp", "tcp4", "tcp6": // https://pkg.go.dev/net#Listen
case "unix":
if m.SocketMode == "" {
errs = append(errs, ErrNoMetricsSocketMode)
}
if _, err := strconv.ParseUint(m.SocketMode, 8, 0); err != nil {
errs = append(errs, fmt.Errorf("%w: %w", ErrInvalidMetricsSocketMode, err))
}
default:
errs = append(errs, ErrInvalidMetricsNetwork)
}
if len(errs) != 0 {
return errors.Join(ErrInvalidMetricsConfig, errors.Join(errs...))
}
return nil
}
-87
View File
@@ -1,87 +0,0 @@
package config
import (
"errors"
"testing"
)
func TestMetricsValid(t *testing.T) {
for _, tt := range []struct {
name string
input *Metrics
err error
}{
{
name: "basic TCP",
input: &Metrics{
Bind: ":9090",
Network: "tcp",
},
},
{
name: "basic TCP4",
input: &Metrics{
Bind: ":9090",
Network: "tcp4",
},
},
{
name: "basic TCP6",
input: &Metrics{
Bind: ":9090",
Network: "tcp6",
},
},
{
name: "basic unix",
input: &Metrics{
Bind: "/tmp/anubis-metrics.sock",
Network: "unix",
SocketMode: "0770",
},
},
{
name: "no bind",
input: &Metrics{},
err: ErrNoMetricsBind,
},
{
name: "no network",
input: &Metrics{},
err: ErrNoMetricsNetwork,
},
{
name: "no unix socket mode",
input: &Metrics{
Bind: "/tmp/anubis-metrics.sock",
Network: "unix",
},
err: ErrNoMetricsSocketMode,
},
{
name: "invalid unix socket mode",
input: &Metrics{
Bind: "/tmp/anubis-metrics.sock",
Network: "unix",
SocketMode: "taco bell",
},
err: ErrInvalidMetricsSocketMode,
},
{
name: "invalid network",
input: &Metrics{
Bind: ":9090",
Network: "taco",
},
err: ErrInvalidMetricsNetwork,
},
} {
t.Run(tt.name, func(t *testing.T) {
if err := tt.input.Valid(); !errors.Is(err, tt.err) {
t.Logf("wanted error: %v", tt.err)
t.Logf("got error: %v", err)
t.Error("validation failed")
}
})
}
}
-3
View File
@@ -1,3 +0,0 @@
metrics:
bind: ":9090"
network: taco
+1 -5
View File
@@ -5,9 +5,5 @@
"remote_addresses": ["0.0.0.0/0", "::/0"], "remote_addresses": ["0.0.0.0/0", "::/0"],
"action": "ALLOW" "action": "ALLOW"
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -4,7 +4,3 @@ bots:
- "0.0.0.0/0" - "0.0.0.0/0"
- "::/0" - "::/0"
action: ALLOW action: ALLOW
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -8,9 +8,5 @@
"action": "DENY" "action": "DENY"
} }
], ],
"dnsbl": false, "dnsbl": false
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -3,7 +3,3 @@ bots:
headers_regex: headers_regex:
CF-Worker: .* CF-Worker: .*
action: DENY action: DENY
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -4,7 +4,3 @@ bots:
asns: asns:
match: match:
- 13335 # Cloudflare - 13335 # Cloudflare
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -5,9 +5,5 @@
"user_agent_regex": "Mozilla", "user_agent_regex": "Mozilla",
"action": "CHALLENGE" "action": "CHALLENGE"
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -2,7 +2,3 @@ bots:
- name: generic-browser - name: generic-browser
user_agent_regex: Mozilla user_agent_regex: Mozilla
action: CHALLENGE action: CHALLENGE
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -6,7 +6,3 @@ bots:
- name: "test" - name: "test"
user_agent_regex: ".*" user_agent_regex: ".*"
action: "DENY" action: "DENY"
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -6,7 +6,3 @@ bots:
- '"Accept" in headers' - '"Accept" in headers'
- headers["Accept"].contains("text/html") - headers["Accept"].contains("text/html")
- randInt(1) == 0 - randInt(1) == 0
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -6,9 +6,5 @@
"action": "DENY" "action": "DENY"
} }
], ],
"dnsbl": false, "dnsbl": false
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -2,7 +2,3 @@ bots:
- name: everything - name: everything
user_agent_regex: .* user_agent_regex: .*
action: DENY action: DENY
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -4,7 +4,3 @@ bots:
geoip: geoip:
countries: countries:
- US - US
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -10,9 +10,5 @@
] ]
} }
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -6,7 +6,3 @@ bots:
- userAgent.startsWith("git/") || userAgent.contains("libgit") - userAgent.startsWith("git/") || userAgent.contains("libgit")
- > - >
"Git-Protocol" in headers && headers["Git-Protocol"] == "version=2" "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -3,9 +3,5 @@
{ {
"import": "./testdata/hack-test.json" "import": "./testdata/hack-test.json"
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -1,6 +1,2 @@
bots: bots:
- import: ./testdata/hack-test.yaml - import: ./testdata/hack-test.yaml
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -3,9 +3,5 @@
{ {
"import": "(data)/common/keep-internet-working.yaml" "import": "(data)/common/keep-internet-working.yaml"
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
@@ -1,6 +1,2 @@
bots: bots:
- import: (data)/common/keep-internet-working.yaml - import: (data)/common/keep-internet-working.yaml
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -8,7 +8,3 @@ impressum:
page: page:
title: Test title: Test
body: <p>This is a test</p> body: <p>This is a test</p>
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -13,7 +13,3 @@ logs:
oldFileTimeFormat: 2006-01-02T15-04-05 # RFC 3339-ish oldFileTimeFormat: 2006-01-02T15-04-05 # RFC 3339-ish
compress: true compress: true
useLocalTime: false # timezone for rotated files is UTC useLocalTime: false # timezone for rotated files is UTC
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -5,7 +5,3 @@ bots:
logging: logging:
sink: "stdio" sink: "stdio"
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -6,7 +6,3 @@ bots:
adjust: 5 adjust: 5
thresholds: [] thresholds: []
metrics:
bind: ":9090"
network: "tcp"
+1 -5
View File
@@ -75,9 +75,5 @@
"user_agent_regex": "Mozilla", "user_agent_regex": "Mozilla",
"action": "CHALLENGE" "action": "CHALLENGE"
} }
], ]
"metrics": {
"bind": ":9090",
"network": "tcp"
}
} }
-4
View File
@@ -10,7 +10,3 @@ openGraph:
default: default:
"og:title": "Xe's magic land of fun" "og:title": "Xe's magic land of fun"
"og:description": "We're no strangers to love, you know the rules and so do I" "og:description": "We're no strangers to love, you know the rules and so do I"
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -4,7 +4,3 @@ bots:
user_agent_regex: Mozilla user_agent_regex: Mozilla
weight: weight:
adjust: 5 adjust: 5
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -9,9 +9,5 @@
"status_codes": { "status_codes": {
"CHALLENGE": 200, "CHALLENGE": 200,
"DENY": 200 "DENY": 200
},
"metrics": {
"bind": ":9090",
"network": "tcp"
} }
} }
-4
View File
@@ -6,7 +6,3 @@ bots:
status_codes: status_codes:
CHALLENGE: 200 CHALLENGE: 200
DENY: 200 DENY: 200
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -9,9 +9,5 @@
"status_codes": { "status_codes": {
"CHALLENGE": 403, "CHALLENGE": 403,
"DENY": 403 "DENY": 403
},
"metrics": {
"bind": ":9090",
"network": "tcp"
} }
} }
-4
View File
@@ -6,7 +6,3 @@ bots:
status_codes: status_codes:
CHALLENGE: 403 CHALLENGE: 403
DENY: 403 DENY: 403
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -33,7 +33,3 @@ thresholds:
challenge: challenge:
algorithm: fast algorithm: fast
difficulty: 4 difficulty: 4
metrics:
bind: ":9090"
network: "tcp"
-4
View File
@@ -2,7 +2,3 @@ bots:
- name: weight - name: weight
action: WEIGH action: WEIGH
user_agent_regex: Mozilla user_agent_regex: Mozilla
metrics:
bind: ":9090"
network: "tcp"
-81
View File
@@ -1,81 +0,0 @@
package metrics
import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"net/http/pprof"
"time"
"github.com/TecharoHQ/anubis/internal"
"github.com/TecharoHQ/anubis/lib/config"
"github.com/prometheus/client_golang/prometheus/promhttp"
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
)
type Server struct {
Config *config.Metrics
Log *slog.Logger
}
func (s *Server) Run(ctx context.Context, done func()) error {
defer done()
lg := s.Log.With("subsystem", "metrics")
mux := http.NewServeMux()
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
st, ok := internal.GetHealth("anubis")
if !ok {
slog.Error("health service anubis does not exist, file a bug")
}
switch st {
case healthv1.HealthCheckResponse_NOT_SERVING:
http.Error(w, "NOT OK", http.StatusInternalServerError)
return
case healthv1.HealthCheckResponse_SERVING:
fmt.Fprintln(w, "OK")
return
default:
http.Error(w, "UNKNOWN", http.StatusFailedDependency)
return
}
})
srv := http.Server{
Handler: mux,
ErrorLog: internal.GetFilteredHTTPLogger(),
}
ln, metricsURL, err := internal.SetupListener(s.Config.Bind, s.Config.Network, s.Config.SocketMode)
if err != nil {
return fmt.Errorf("can't setup listener: %w", err)
}
defer ln.Close()
lg.Debug("listening for metrics", "url", metricsURL)
go func() {
<-ctx.Done()
c, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(c); err != nil {
lg.Error("can't shut down metrics server", "err", err)
}
}()
if err := srv.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
return fmt.Errorf("can't serve metrics server: %w", err)
}
return nil
}
+2 -4
View File
@@ -46,7 +46,6 @@ type ParsedConfig struct {
DnsCache *dns.DnsCache DnsCache *dns.DnsCache
Dns *dns.Dns Dns *dns.Dns
Logger *slog.Logger Logger *slog.Logger
Metrics *config.Metrics
} }
func newParsedConfig(orig *config.Config) *ParsedConfig { func newParsedConfig(orig *config.Config) *ParsedConfig {
@@ -54,7 +53,6 @@ func newParsedConfig(orig *config.Config) *ParsedConfig {
orig: orig, orig: orig,
OpenGraph: orig.OpenGraph, OpenGraph: orig.OpenGraph,
StatusCodes: orig.StatusCodes, StatusCodes: orig.StatusCodes,
Metrics: orig.Metrics,
} }
} }
@@ -77,7 +75,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
switch c.Logging.Sink { switch c.Logging.Sink {
case config.LogSinkStdio: case config.LogSinkStdio:
result.Logger = internal.InitSlog(logLevel, os.Stderr) result.Logger = internal.InitSlog(logLevel, os.Stderr, c.Logging.HideSource)
case config.LogSinkFile: case config.LogSinkFile:
out := &logrotate.Logger{ out := &logrotate.Logger{
Filename: c.Logging.Parameters.Filename, Filename: c.Logging.Parameters.Filename,
@@ -89,7 +87,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
Compress: c.Logging.Parameters.Compress, Compress: c.Logging.Parameters.Compress,
} }
result.Logger = internal.InitSlog(logLevel, out) result.Logger = internal.InitSlog(logLevel, out, c.Logging.HideSource)
} }
lg := result.Logger.With("at", "config-validate") lg := result.Logger.With("at", "config-validate")
+160 -160
View File
@@ -10,15 +10,15 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/sha256-js": "^5.2.0",
"preact": "^10.29.1" "preact": "^10.28.4"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^20.5.0", "@commitlint/cli": "^20.4.3",
"@commitlint/config-conventional": "^20.5.0", "@commitlint/config-conventional": "^20.4.3",
"baseline-browser-mapping": "^2.10.15", "baseline-browser-mapping": "^2.10.0",
"cssnano": "^7.1.4", "cssnano": "^7.1.3",
"cssnano-preset-advanced": "^7.0.12", "cssnano-preset-advanced": "^7.0.11",
"esbuild": "^0.28.0", "esbuild": "^0.27.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"playwright": "^1.52.0", "playwright": "^1.52.0",
"postcss-cli": "^11.0.1", "postcss-cli": "^11.0.1",
@@ -91,25 +91,18 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@colordx/core": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.0.3.tgz",
"integrity": "sha512-xBQ0MYRTNNxW3mS2sJtlQTT7C3Sasqgh1/PsHva7fyDb5uqYY+gv9V0utDdX8X80mqzbGz3u/IDJdn2d/uW09g==",
"dev": true,
"license": "MIT"
},
"node_modules/@commitlint/cli": { "node_modules/@commitlint/cli": {
"version": "20.5.0", "version": "20.4.3",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.0.tgz", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.3.tgz",
"integrity": "sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==", "integrity": "sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@commitlint/format": "^20.5.0", "@commitlint/format": "^20.4.3",
"@commitlint/lint": "^20.5.0", "@commitlint/lint": "^20.4.3",
"@commitlint/load": "^20.5.0", "@commitlint/load": "^20.4.3",
"@commitlint/read": "^20.5.0", "@commitlint/read": "^20.4.3",
"@commitlint/types": "^20.5.0", "@commitlint/types": "^20.4.3",
"tinyexec": "^1.0.0", "tinyexec": "^1.0.0",
"yargs": "^17.0.0" "yargs": "^17.0.0"
}, },
@@ -121,13 +114,13 @@
} }
}, },
"node_modules/@commitlint/config-conventional": { "node_modules/@commitlint/config-conventional": {
"version": "20.5.0", "version": "20.4.3",
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.0.tgz", "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.3.tgz",
"integrity": "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==", "integrity": "sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@commitlint/types": "^20.5.0", "@commitlint/types": "^20.4.3",
"conventional-changelog-conventionalcommits": "^9.2.0" "conventional-changelog-conventionalcommits": "^9.2.0"
}, },
"engines": { "engines": {
@@ -408,9 +401,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -425,9 +418,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -442,9 +435,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -459,9 +452,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -476,9 +469,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -493,9 +486,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -510,9 +503,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -527,9 +520,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -544,9 +537,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -561,9 +554,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -578,9 +571,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -595,9 +588,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -612,9 +605,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@@ -629,9 +622,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -646,9 +639,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -663,9 +656,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -680,9 +673,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -697,9 +690,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -714,9 +707,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -731,9 +724,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -748,9 +741,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -765,9 +758,9 @@
} }
}, },
"node_modules/@esbuild/openharmony-arm64": { "node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -782,9 +775,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -799,9 +792,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -816,9 +809,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -833,9 +826,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1055,9 +1048,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.10.15", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.15.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
"integrity": "sha512-1nfKCq9wuAZFTkA2ey/3OXXx7GzFjLdkTiFVNwlJ9WqdI706CZRIhEqjuwanjMIja+84jDLa9rcyZDPDiVkASQ==", "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -1249,6 +1242,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/colord": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true,
"license": "MIT"
},
"node_modules/commander": { "node_modules/commander": {
"version": "11.1.0", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
@@ -1436,13 +1436,13 @@
} }
}, },
"node_modules/cssnano": { "node_modules/cssnano": {
"version": "7.1.4", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.4.tgz", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.3.tgz",
"integrity": "sha512-T9PNS7y+5Nc9Qmu9mRONqfxG1RVY7Vuvky0XN6MZ+9hqplesTEwnj9r0ROtVuSwUVfaDhVlavuzWIVLUgm4hkQ==", "integrity": "sha512-mLFHQAzyapMVFLiJIn7Ef4C2UCEvtlTlbyILR6B5ZsUAV3D/Pa761R5uC1YPhyBkRd3eqaDm2ncaNrD7R4mTRg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cssnano-preset-default": "^7.0.12", "cssnano-preset-default": "^7.0.11",
"lilconfig": "^3.1.3" "lilconfig": "^3.1.3"
}, },
"engines": { "engines": {
@@ -1457,15 +1457,15 @@
} }
}, },
"node_modules/cssnano-preset-advanced": { "node_modules/cssnano-preset-advanced": {
"version": "7.0.12", "version": "7.0.11",
"resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.12.tgz", "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.11.tgz",
"integrity": "sha512-S+UCfEMBCaYqNmjY+yCTNh4ur+B83z5qpwkzUMER6kVpdQ5CBUxIkALZGhp2KPP3M4VefRCIBMqJ6Z0PFMwvEA==", "integrity": "sha512-k9Dz/8TBOmLojoMZ+2xwrGjscU1XwYVDVB9T1AmvZcwiWz4ibWm8/y+/SN5jhYcpMPTeDe7FhI2uEYlNq1iDGg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"autoprefixer": "^10.4.27", "autoprefixer": "^10.4.27",
"browserslist": "^4.28.1", "browserslist": "^4.28.1",
"cssnano-preset-default": "^7.0.12", "cssnano-preset-default": "^7.0.11",
"postcss-discard-unused": "^7.0.5", "postcss-discard-unused": "^7.0.5",
"postcss-merge-idents": "^7.0.1", "postcss-merge-idents": "^7.0.1",
"postcss-reduce-idents": "^7.0.1", "postcss-reduce-idents": "^7.0.1",
@@ -1479,9 +1479,9 @@
} }
}, },
"node_modules/cssnano-preset-default": { "node_modules/cssnano-preset-default": {
"version": "7.0.12", "version": "7.0.11",
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.12.tgz", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.11.tgz",
"integrity": "sha512-B3Eoouzw/sl2zANI0AL9KbacummJTCww+fkHaDBMZad/xuVx8bUduPLly6hKVQAlrmvYkS1jB1CVQEKm3gn0AA==", "integrity": "sha512-waWlAMuCakP7//UCY+JPrQS1z0OSLeOXk2sKWJximKWGupVxre50bzPlvpbUwZIDylhf/ptf0Pk+Yf7C+hoa3g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1489,7 +1489,7 @@
"css-declaration-sorter": "^7.2.0", "css-declaration-sorter": "^7.2.0",
"cssnano-utils": "^5.0.1", "cssnano-utils": "^5.0.1",
"postcss-calc": "^10.1.1", "postcss-calc": "^10.1.1",
"postcss-colormin": "^7.0.7", "postcss-colormin": "^7.0.6",
"postcss-convert-values": "^7.0.9", "postcss-convert-values": "^7.0.9",
"postcss-discard-comments": "^7.0.6", "postcss-discard-comments": "^7.0.6",
"postcss-discard-duplicates": "^7.0.2", "postcss-discard-duplicates": "^7.0.2",
@@ -1498,7 +1498,7 @@
"postcss-merge-longhand": "^7.0.5", "postcss-merge-longhand": "^7.0.5",
"postcss-merge-rules": "^7.0.8", "postcss-merge-rules": "^7.0.8",
"postcss-minify-font-values": "^7.0.1", "postcss-minify-font-values": "^7.0.1",
"postcss-minify-gradients": "^7.0.2", "postcss-minify-gradients": "^7.0.1",
"postcss-minify-params": "^7.0.6", "postcss-minify-params": "^7.0.6",
"postcss-minify-selectors": "^7.0.6", "postcss-minify-selectors": "^7.0.6",
"postcss-normalize-charset": "^7.0.1", "postcss-normalize-charset": "^7.0.1",
@@ -1709,9 +1709,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.28.0", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -1722,32 +1722,32 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.0", "@esbuild/aix-ppc64": "0.27.3",
"@esbuild/android-arm": "0.28.0", "@esbuild/android-arm": "0.27.3",
"@esbuild/android-arm64": "0.28.0", "@esbuild/android-arm64": "0.27.3",
"@esbuild/android-x64": "0.28.0", "@esbuild/android-x64": "0.27.3",
"@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-arm64": "0.27.3",
"@esbuild/darwin-x64": "0.28.0", "@esbuild/darwin-x64": "0.27.3",
"@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-arm64": "0.27.3",
"@esbuild/freebsd-x64": "0.28.0", "@esbuild/freebsd-x64": "0.27.3",
"@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm": "0.27.3",
"@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-arm64": "0.27.3",
"@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-ia32": "0.27.3",
"@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-loong64": "0.27.3",
"@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-mips64el": "0.27.3",
"@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-ppc64": "0.27.3",
"@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-riscv64": "0.27.3",
"@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-s390x": "0.27.3",
"@esbuild/linux-x64": "0.28.0", "@esbuild/linux-x64": "0.27.3",
"@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-arm64": "0.27.3",
"@esbuild/netbsd-x64": "0.28.0", "@esbuild/netbsd-x64": "0.27.3",
"@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-arm64": "0.27.3",
"@esbuild/openbsd-x64": "0.28.0", "@esbuild/openbsd-x64": "0.27.3",
"@esbuild/openharmony-arm64": "0.28.0", "@esbuild/openharmony-arm64": "0.27.3",
"@esbuild/sunos-x64": "0.28.0", "@esbuild/sunos-x64": "0.27.3",
"@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-arm64": "0.27.3",
"@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-ia32": "0.27.3",
"@esbuild/win32-x64": "0.28.0" "@esbuild/win32-x64": "0.27.3"
} }
}, },
"node_modules/escalade": { "node_modules/escalade": {
@@ -2554,15 +2554,15 @@
} }
}, },
"node_modules/postcss-colormin": { "node_modules/postcss-colormin": {
"version": "7.0.7", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.7.tgz", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.6.tgz",
"integrity": "sha512-sBQ628lSj3VQpDquQel8Pen5mmjFPsO4pH9lDLaHB1AVkMRHtkl0pRB5DCWznc9upWsxint/kV+AveSj7W1tew==", "integrity": "sha512-oXM2mdx6IBTRm39797QguYzVEWzbdlFiMNfq88fCCN1Wepw3CYmJ/1/Ifa/KjWo+j5ZURDl2NTldLJIw51IeNQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@colordx/core": "^5.0.0",
"browserslist": "^4.28.1", "browserslist": "^4.28.1",
"caniuse-api": "^3.0.0", "caniuse-api": "^3.0.0",
"colord": "^2.9.3",
"postcss-value-parser": "^4.2.0" "postcss-value-parser": "^4.2.0"
}, },
"engines": { "engines": {
@@ -2808,13 +2808,13 @@
} }
}, },
"node_modules/postcss-minify-gradients": { "node_modules/postcss-minify-gradients": {
"version": "7.0.2", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.2.tgz", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz",
"integrity": "sha512-fVY3AB8Um7SJR5usHqTY2Ngf9qh8IRN+FFzrBP0ONJy6yYXsP7xyjK2BvSAIrpgs1cST+H91V0TXi3diHLYJtw==", "integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@colordx/core": "^5.0.0", "colord": "^2.9.3",
"cssnano-utils": "^5.0.1", "cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0" "postcss-value-parser": "^4.2.0"
}, },
@@ -3182,9 +3182,9 @@
} }
}, },
"node_modules/preact": { "node_modules/preact": {
"version": "10.29.1", "version": "10.28.4",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.4.tgz",
"integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
+7 -7
View File
@@ -20,12 +20,12 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^20.5.0", "@commitlint/cli": "^20.4.3",
"@commitlint/config-conventional": "^20.5.0", "@commitlint/config-conventional": "^20.4.3",
"baseline-browser-mapping": "^2.10.15", "baseline-browser-mapping": "^2.10.0",
"cssnano": "^7.1.4", "cssnano": "^7.1.3",
"cssnano-preset-advanced": "^7.0.12", "cssnano-preset-advanced": "^7.0.11",
"esbuild": "^0.28.0", "esbuild": "^0.27.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"playwright": "^1.52.0", "playwright": "^1.52.0",
"postcss-cli": "^11.0.1", "postcss-cli": "^11.0.1",
@@ -36,7 +36,7 @@
}, },
"dependencies": { "dependencies": {
"@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/sha256-js": "^5.2.0",
"preact": "^10.29.1" "preact": "^10.28.4"
}, },
"commitlint": { "commitlint": {
"extends": [ "extends": [