4. Resulting Hash (SHA-256)
@@ -193,18 +267,30 @@ export default function App() {
{/* Mining Controls */}
{!isMining ? (
-
diff --git a/docs/blog/2025-08-28-cpu-core-odd/ProofOfWorkDiagram/styles.module.css b/docs/blog/2025-08-28-cpu-core-odd/ProofOfWorkDiagram/styles.module.css
index 419b4048..157f8402 100644
--- a/docs/blog/2025-08-28-cpu-core-odd/ProofOfWorkDiagram/styles.module.css
+++ b/docs/blog/2025-08-28-cpu-core-odd/ProofOfWorkDiagram/styles.module.css
@@ -48,7 +48,9 @@
background-color: rgb(31 41 55);
padding: 1.5rem;
border-radius: 0.5rem;
- box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ box-shadow:
+ 0 10px 15px -3px rgb(0 0 0 / 0.1),
+ 0 4px 6px -4px rgb(0 0 0 / 0.1);
height: 100%;
display: flex;
flex-direction: column;
@@ -158,7 +160,9 @@
.hashContainer {
padding: 1.5rem;
border-radius: 0.5rem;
- box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ box-shadow:
+ 0 10px 15px -3px rgb(0 0 0 / 0.1),
+ 0 4px 6px -4px rgb(0 0 0 / 0.1);
transition: all 300ms;
border: 2px solid;
}
diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md
index 175e001b..a96b8e6f 100644
--- a/docs/docs/CHANGELOG.md
+++ b/docs/docs/CHANGELOG.md
@@ -11,11 +11,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+- fix: prevent nil pointer panic in challenge validation when threshold rules match during PassChallenge (#1463)
+- Instruct reverse proxies to not cache error pages.
+- Fixed mixed tab/space indentation in Caddy documentation code block
+
+
+- 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
+
+Hey all,
+
+I'm sure you've all been aware that things have been slowing down a little with Anubis development, and I want to apologize for that. A lot has been going on in my life lately (my blog will have a post out on Friday with more information), and as a result I haven't really had the energy to work on Anubis in publicly visible ways. There are things going on behind the scenes, but nothing is really shippable yet, sorry!
+
+I've also been feeling some burnout in the wake of perennial waves of anger directed towards me. I'm handling it, I'll be fine, I've just had a lot going on in my life and it's been rough.
+
+I've been missing the sense of wanderlust and discovery that comes with the artistic way I playfully develop software. I suspect that some of the stresses I've been through (setting up a complicated surgery in a country whose language you aren't fluent in is kind of an experience) have been sapping my energy. I'd gonna try to mess with things on my break, but realistically I'm probably just gonna be either watching Stargate SG-1 or doing unreasonable amounts of ocean fishing in Final Fantasy 14. Normally I'd love to keep the details about my medical state fairly private, but I'm more of a public figure now than I was this time last year so I don't really get the invisibility I'm used to for this.
+
+I've also had a fair amount of negativity directed at me for simply being much more visible than the anonymous threat actors running the scrapers that are ruining everything, which though understandable has not helped.
+
+Anyways, it all worked out and I'm about to be in the hospital for a week, so if things go really badly with this release please downgrade to the last version and/or upgrade to the main branch when the fix PR is inevitably merged. I hoped to have time to tame GPG and set up full release automation in the Anubis repo, but that didn't work out this time and that's okay.
+
+If I can challenge you all to do something, go out there and try to actually create something new somehow. Combine ideas you've never mixed before. Be creative, be human, make something purely for yourself to scratch an itch that you've always had yet never gotten around to actually mending.
+
+At the very least, try to be an example of how you want other people to act, even when you're in a situation where software written by someone else is configured to require a user agent to execute javascript to access a webpage.
+
+Be well,
+
+Xe
+
+PS: if you're well-versed in FFXIV lore, the release title should give you an idea of the kind of stuff I've been going through mentally.
+
- Add iplist2rule tool that lets admins turn an IP address blocklist into an Anubis ruleset.
- Add Polish locale ([#1292](https://github.com/TecharoHQ/anubis/pull/1309))
- Fix honeypot and imprint links missing `BASE_PREFIX` when deployed behind a path prefix ([#1402](https://github.com/TecharoHQ/anubis/issues/1402))
-
-
+- Add ANEXIA Sponsor logo to docs ([#1409](https://github.com/TecharoHQ/anubis/pull/1409))
+- Improve idle performance in memory storage
+- Add HAProxy Configurations to Docs ([#1424](https://github.com/TecharoHQ/anubis/pull/1424))
## v1.24.0: Y'shtola Rhul
diff --git a/docs/docs/admin/_category_.json b/docs/docs/admin/_category_.json
index 4b7ba50a..a5586551 100644
--- a/docs/docs/admin/_category_.json
+++ b/docs/docs/admin/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Tradeoffs and considerations you may want to keep in mind when using Anubis."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/configuration/_category_.json b/docs/docs/admin/configuration/_category_.json
index e22c7a2f..6b797b2b 100644
--- a/docs/docs/admin/configuration/_category_.json
+++ b/docs/docs/admin/configuration/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Detailed information about configuring parts of Anubis."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/configuration/challenges/_category_.json b/docs/docs/admin/configuration/challenges/_category_.json
index 96a70d2f..a01e84f1 100644
--- a/docs/docs/admin/configuration/challenges/_category_.json
+++ b/docs/docs/admin/configuration/challenges/_category_.json
@@ -2,4 +2,4 @@
"label": "Challenges",
"position": 10,
"link": null
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/configuration/expressions.mdx b/docs/docs/admin/configuration/expressions.mdx
index 762b848b..109b9c0a 100644
--- a/docs/docs/admin/configuration/expressions.mdx
+++ b/docs/docs/admin/configuration/expressions.mdx
@@ -243,16 +243,16 @@ function regexSafe(input: string): string;
`regexSafe` takes a string and escapes it for safe use inside of a regular expression. This is useful when you are creating regular expressions from headers or variables such as `remoteAddress`.
-| Input | Output |
-| :------------------------ | :------------------------------ |
-| `regexSafe("1.2.3.4")` | `1\\.2\\.3\\.4` |
-| `regexSafe("techaro.lol")` | `techaro\\.lol` |
-| `regexSafe("star*")` | `star\\*` |
-| `regexSafe("plus+")` | `plus\\+` |
-| `regexSafe("{braces}")` | `\\{braces\\}` |
-| `regexSafe("start^")` | `start\\^` |
-| `regexSafe("back\\slash")` | `back\\\\slash` |
-| `regexSafe("dash-dash")` | `dash\\-dash` |
+| Input | Output |
+| :------------------------- | :-------------- |
+| `regexSafe("1.2.3.4")` | `1\\.2\\.3\\.4` |
+| `regexSafe("techaro.lol")` | `techaro\\.lol` |
+| `regexSafe("star*")` | `star\\*` |
+| `regexSafe("plus+")` | `plus\\+` |
+| `regexSafe("{braces}")` | `\\{braces\\}` |
+| `regexSafe("start^")` | `start\\^` |
+| `regexSafe("back\\slash")` | `back\\\\slash` |
+| `regexSafe("dash-dash")` | `dash\\-dash` |
### `segments`
@@ -301,9 +301,9 @@ function arpaReverseIP(ip: string): string;
`arpaReverseIP` takes an IP address and returns its value in [ARPA notation](https://www.ietf.org/rfc/rfc2317.html). This can be useful when matching PTR record patterns.
-| Input | Output |
-| :----------------------------- | :------------------------------------------------------------------- |
-| `arpaReverseIP("1.2.3.4")` | `4.3.2.1` |
+| Input | Output |
+| :----------------------------- | :---------------------------------------------------------------- |
+| `arpaReverseIP("1.2.3.4")` | `4.3.2.1` |
| `arpaReverseIP("2001:db8::1")` | `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2` |
#### `lookupHost`
diff --git a/docs/docs/admin/default-allow-behavior.mdx b/docs/docs/admin/default-allow-behavior.mdx
index 6249e735..1b87183e 100644
--- a/docs/docs/admin/default-allow-behavior.mdx
+++ b/docs/docs/admin/default-allow-behavior.mdx
@@ -89,4 +89,4 @@ If you want to deny all traffic except what you explicitly allow, add a catch-al
- The implicit allow rule is always last and cannot be removed.
- Use your logs to monitor what traffic is being allowed by default.
-See [Policy Definitions](./policies) for more details on writing rules.
\ No newline at end of file
+See [Policy Definitions](./policies) for more details on writing rules.
diff --git a/docs/docs/admin/environments/_category_.json b/docs/docs/admin/environments/_category_.json
index 31522719..a9150a73 100644
--- a/docs/docs/admin/environments/_category_.json
+++ b/docs/docs/admin/environments/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Detailed information about individual environments (such as HTTP servers, platforms, etc.) Anubis is known to work with."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/environments/caddy.mdx b/docs/docs/admin/environments/caddy.mdx
index 3e5343a1..30edec17 100644
--- a/docs/docs/admin/environments/caddy.mdx
+++ b/docs/docs/admin/environments/caddy.mdx
@@ -62,9 +62,9 @@ yourdomain.example.com {
tls your@email.address
reverse_proxy http://anubis:3000 {
- header_up X-Real-Ip {remote_host}
- header_up X-Http-Version {http.request.proto}
- }
+ header_up X-Real-Ip {remote_host}
+ header_up X-Http-Version {http.request.proto}
+ }
}
```
diff --git a/docs/docs/admin/environments/haproxy.mdx b/docs/docs/admin/environments/haproxy.mdx
new file mode 100644
index 00000000..3acc5766
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy.mdx
@@ -0,0 +1,101 @@
+# HAProxy
+
+import CodeBlock from "@theme/CodeBlock";
+
+To use Anubis with HAProxy, you have two variants:
+ - simple - stick Anubis between HAProxy and your application backend (simple)
+ - perfect if you only have a single application in general
+ - advanced - force Anubis challenge by default and route to the application backend by HAProxy if the challenge is correct
+ - useful for complex setups
+ - routing can be done in HAProxy
+ - define ACLs in HAProxy for domains, paths etc which are required/excluded regarding Anubis
+ - HAProxy 3.0 recommended
+
+## Simple Variant
+
+```mermaid
+---
+title: HAProxy with simple config
+---
+flowchart LR
+ T(User Traffic)
+ HAProxy(HAProxy Port 80/443)
+ Anubis
+ Application
+
+ T --> HAProxy
+ HAProxy --> Anubis
+ Anubis --> |Happy Traffic| Application
+```
+
+Your Anubis env file configuration may look like this:
+
+import simpleAnubis from "!!raw-loader!./haproxy/simple-config.env";
+
+
{simpleAnubis}
+
+The important part is that `TARGET` points to your actual application and if Anubis and HAProxy are on the same machine, a UNIX socket can be used.
+
+Your frontend and backend configuration of HAProxy may look like the following:
+
+import simpleHAProxy from "!!raw-loader!./haproxy/simple-haproxy.cfg";
+
+
{simpleHAProxy}
+
+This simply enables SSL offloading, sets some useful and required headers and routes to Anubis directly.
+
+## Advanced Variant
+
+Due to the fact that HAProxy can decode JWT, we are able to verify the Anubis token directly in HAProxy and route the traffic to the specific backends ourselves.
+
+Mind that rule logic to allow Git HTTP and other legit bot traffic to bypass is delegated from Anubis to HAProxy then. If required, you should implement any whitelisting in HAProxy using `acl_anubis_ignore` yourself.
+
+In this example are three applications behind one HAProxy frontend. Only App1 and App2 are secured via Anubis; App3 is open for everyone. The path `/excluded/path` can also be accessed by anyone.
+
+```mermaid
+---
+title: HAProxy with advanced config
+---
+
+flowchart LR
+ T(User Traffic)
+ HAProxy(HAProxy Port 80/443)
+ B1(App1)
+ B2(App2)
+ B3(App3)
+ Anubis
+
+ T --> HAProxy
+ HAProxy --> |Traffic for App1 and App2 without valid challenge| Anubis
+ HAProxy --> |app1.example.com | B1
+ HAProxy --> |app2.example.com| B2
+ HAProxy --> |app3.example.com| B3
+```
+
+:::note
+
+For an improved JWT decoding performance, it's recommended to use HAProxy version 3.0 or above.
+
+:::
+
+Your Anubis env file configuration may look like this:
+
+import advancedAnubis from "!!raw-loader!./haproxy/advanced-config.env";
+
+
{advancedAnubis}
+
+It's important to use `HS512_SECRET` which HAProxy understands. Please replace `
` with your own secret string (alphanumerical string with 128 characters recommended).
+
+You can set Anubis to force a challenge for every request using the following policy file:
+
+import advancedAnubisPolicy from "!!raw-loader!./haproxy/advanced-config-policy.yml";
+
+{advancedAnubisPolicy}
+
+The HAProxy config file may look like this:
+
+import advancedHAProxy from "!!raw-loader!./haproxy/advanced-haproxy.cfg";
+
+{advancedHAProxy}
+
+Please replace `` with the same secret from the Anubis config.
diff --git a/docs/docs/admin/environments/haproxy/advanced-config-policy.yml b/docs/docs/admin/environments/haproxy/advanced-config-policy.yml
new file mode 100644
index 00000000..93c06dc3
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy/advanced-config-policy.yml
@@ -0,0 +1,15 @@
+# /etc/anubis/challenge-any.yml
+
+bots:
+ - name: any
+ action: CHALLENGE
+ user_agent_regex: .*
+
+status_codes:
+ CHALLENGE: 403
+ DENY: 403
+
+thresholds: []
+
+dnsbl: false
+
diff --git a/docs/docs/admin/environments/haproxy/advanced-config.env b/docs/docs/admin/environments/haproxy/advanced-config.env
new file mode 100644
index 00000000..712fdeeb
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy/advanced-config.env
@@ -0,0 +1,11 @@
+# /etc/anubis/default.env
+
+BIND=/run/anubis/default.sock
+BIND_NETWORK=unix
+DIFFICULTY=4
+METRICS_BIND=:9090
+# target is irrelevant here, backend routing happens in HAProxy
+TARGET=http://0.0.0.0
+HS512_SECRET=
+COOKIE_DYNAMIC_DOMAIN=True
+POLICY_FNAME=/etc/anubis/challenge-any.yml
diff --git a/docs/docs/admin/environments/haproxy/advanced-haproxy.cfg b/docs/docs/admin/environments/haproxy/advanced-haproxy.cfg
new file mode 100644
index 00000000..1540067b
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy/advanced-haproxy.cfg
@@ -0,0 +1,59 @@
+# /etc/haproxy/haproxy.cfg
+
+frontend FE-multiple-applications
+ mode http
+ bind :80
+ # ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
+ bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
+
+ # set X-Real-IP header required for Anubis
+ http-request set-header X-Real-IP "%[src]"
+
+ # redirect HTTP to HTTPS
+ http-request redirect scheme https code 301 unless { ssl_fc }
+ # add HSTS header
+ http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
+
+ # only force Anubis challenge for app1 and app2
+ acl acl_anubis_required hdr(host) -i "app1.example.com"
+ acl acl_anubis_required hdr(host) -i "app2.example.com"
+
+ # exclude Anubis for a specific path
+ acl acl_anubis_ignore path /excluded/path
+
+ # use Anubis if auth cookie not found
+ use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ req.cook(techaro.lol-anubis-auth) -m found }
+
+ # get payload of the JWT such as algorithm, expire time, restrictions
+ http-request set-var(txn.anubis_jwt_alg) req.cook(techaro.lol-anubis-auth),jwt_header_query('$.alg') if acl_anubis_required !acl_anubis_ignore
+ http-request set-var(txn.anubis_jwt_exp) cook(techaro.lol-anubis-auth),jwt_payload_query('$.exp','int') if acl_anubis_required !acl_anubis_ignore
+ http-request set-var(txn.anubis_jwt_res) cook(techaro.lol-anubis-auth),jwt_payload_query('$.restriction') if acl_anubis_required !acl_anubis_ignore
+ http-request set-var(txn.srcip) req.fhdr(X-Real-IP) if acl_anubis_required !acl_anubis_ignore
+ http-request set-var(txn.now) date() if acl_anubis_required !acl_anubis_ignore
+
+ # use Anubis if JWT has wrong algorithm, is expired, restrictions don't match or isn't signed with the correct key
+ use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.anubis_jwt_alg) -m str HS512 }
+ use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore { var(txn.anubis_jwt_exp),sub(txn.now) -m int lt 0 }
+ use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.srcip),digest(sha256),hex,lower,strcmp(txn.anubis_jwt_res) eq 0 }
+ use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ cook(techaro.lol-anubis-auth),jwt_verify(txn.anubis_jwt_alg,"") -m int 1 }
+
+ # custom routing in HAProxy
+ use_backend BE-app1 if { hdr(host) -i "app1.example.com" }
+ use_backend BE-app2 if { hdr(host) -i "app2.example.com" }
+ use_backend BE-app3 if { hdr(host) -i "app3.example.com" }
+
+backend BE-app1
+ mode http
+ server app1-server 127.0.0.1:3000
+
+backend BE-app2
+ mode http
+ server app2-server 127.0.0.1:4000
+
+backend BE-app3
+ mode http
+ server app3-server 127.0.0.1:5000
+
+BE-anubis
+ mode http
+ server anubis /run/anubis/default.sock
diff --git a/docs/docs/admin/environments/haproxy/simple-config.env b/docs/docs/admin/environments/haproxy/simple-config.env
new file mode 100644
index 00000000..02a3f667
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy/simple-config.env
@@ -0,0 +1,10 @@
+# /etc/anubis/default.env
+
+BIND=/run/anubis/default.sock
+BIND_NETWORK=unix
+SOCKET_MODE=0666
+DIFFICULTY=4
+METRICS_BIND=:9090
+COOKIE_DYNAMIC_DOMAIN=true
+# address and port of the actual application
+TARGET=http://localhost:3000
diff --git a/docs/docs/admin/environments/haproxy/simple-haproxy.cfg b/docs/docs/admin/environments/haproxy/simple-haproxy.cfg
new file mode 100644
index 00000000..445d0788
--- /dev/null
+++ b/docs/docs/admin/environments/haproxy/simple-haproxy.cfg
@@ -0,0 +1,22 @@
+# /etc/haproxy/haproxy.cfg
+
+frontend FE-application
+ mode http
+ bind :80
+ # ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
+ bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
+
+ # set X-Real-IP header required for Anubis
+ http-request set-header X-Real-IP "%[src]"
+
+ # redirect HTTP to HTTPS
+ http-request redirect scheme https code 301 unless { ssl_fc }
+ # add HSTS header
+ http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
+
+ # route to Anubis backend by default
+ default_backend BE-anubis-application
+
+BE-anubis-application
+ mode http
+ server anubis /run/anubis/default.sock
diff --git a/docs/docs/admin/environments/kubernetes.mdx b/docs/docs/admin/environments/kubernetes.mdx
index e10f18e2..6b81313e 100644
--- a/docs/docs/admin/environments/kubernetes.mdx
+++ b/docs/docs/admin/environments/kubernetes.mdx
@@ -94,10 +94,8 @@ containers:
- ALL
seccompProfile:
type: RuntimeDefault
-
```
-
Then add a Service entry for Anubis:
```yaml
@@ -132,3 +130,52 @@ Then point your Ingress to the Anubis port:
# diff-add
name: anubis
```
+
+## Envoy Gateway
+
+If you are using envoy-gateway, the `X-Real-Ip` header is not set by default, but Anubis does require it. You can resolve this by adding the header, either on the specific `HTTPRoute` where Anubis is listening, or on the `ClientTrafficPolicy` to apply it to any number of Gateways:
+
+HTTPRoute:
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: app-route
+spec:
+ hostnames: ["app.domain.tld"]
+ parentRefs:
+ - name: envoy-external
+ namespace: network
+ sectionName: https
+ rules:
+ - backendRefs:
+ - identifier: *app
+ port: anubis
+ filters:
+ - type: RequestHeaderModifier
+ requestHeaderModifier:
+ set:
+ - name: X-Real-Ip
+ value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
+```
+
+Applying to any number of Gateways:
+```yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: ClientTrafficPolicy
+metadata:
+ name: envoy
+spec:
+ headers:
+ earlyRequestHeaders:
+ set:
+ - name: X-Real-Ip
+ value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
+ clientIPDetection:
+ xForwardedFor:
+ trustedCIDRs:
+ - 10.96.0.0/16 # Cluster pod CIDR
+ targetSelectors: # These will apply to all Gateways
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+```
diff --git a/docs/docs/admin/environments/nginx/conf-anubis.inc b/docs/docs/admin/environments/nginx/conf-anubis.inc
index 6e5083ae..bc20e8f4 100644
--- a/docs/docs/admin/environments/nginx/conf-anubis.inc
+++ b/docs/docs/admin/environments/nginx/conf-anubis.inc
@@ -1,8 +1,2 @@
-# /etc/nginx/conf-anubis.inc
-
-# Forward to anubis
-location / {
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_pass http://anubis;
-}
\ No newline at end of file
+# /etc/nginx/conf-anubis.inc # Forward to anubis location / { proxy_set_header
+Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://anubis; }
diff --git a/docs/docs/admin/environments/traefik.mdx b/docs/docs/admin/environments/traefik.mdx
index 197ddc19..04df792d 100644
--- a/docs/docs/admin/environments/traefik.mdx
+++ b/docs/docs/admin/environments/traefik.mdx
@@ -75,7 +75,7 @@ services:
# Telling Anubis, where to listen for Traefik
- BIND=:8080
# Telling Anubis to do redirect — ensure there is a space after '='
- - 'TARGET= '
+ - "TARGET= "
# Specifies which domains Anubis is allowed to redirect to.
- REDIRECT_DOMAINS=example.com
# Should be the full external URL for Anubis (including scheme)
diff --git a/docs/docs/admin/frameworks/_category_.json b/docs/docs/admin/frameworks/_category_.json
index 28eefe88..253bf449 100644
--- a/docs/docs/admin/frameworks/_category_.json
+++ b/docs/docs/admin/frameworks/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Information about getting specific frameworks or tools working with Anubis."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/honeypot/_category_.json b/docs/docs/admin/honeypot/_category_.json
index bc0581e9..90c63375 100644
--- a/docs/docs/admin/honeypot/_category_.json
+++ b/docs/docs/admin/honeypot/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Honeypot features in Anubis, allowing Anubis to passively detect malicious crawlers."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/admin/installation.mdx b/docs/docs/admin/installation.mdx
index 7642526e..7000afef 100644
--- a/docs/docs/admin/installation.mdx
+++ b/docs/docs/admin/installation.mdx
@@ -67,7 +67,7 @@ Currently the following settings are configurable via the policy file:
Anubis uses these environment variables for configuration:
| Environment Variable | Default value | Explanation |
-|:-------------------------------|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| :----------------------------- | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ASSET_LOOKUP_HEADER` | unset | If set, use the contents of this header in requests when looking up custom assets in `OVERLAY_FOLDER`. See [Header-based overlay dispatch](./botstopper.mdx#header-based-overlay-dispatch) for more details. |
| `BASE_PREFIX` | unset | If set, adds a global prefix to all Anubis endpoints (everything starting with `/.within.website/x/anubis/`). For example, setting this to `/myapp` would make Anubis accessible at `/myapp/` instead of `/`. This is useful when running Anubis behind a reverse proxy that routes based on path prefixes. |
| `BIND` | `:8923` | The network address that Anubis listens on. For `unix`, set this to a path: `/run/anubis/instance.sock` |
@@ -123,7 +123,7 @@ If you don't know or understand what these settings mean, ignore them. These are
| `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. |
| `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. |
| `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. |
-| `TARGET_SNI` | unset | If set, TLS handshake hostname when forwarding requests to the `TARGET`. If set to auto, use Host header. |
+| `TARGET_SNI` | unset | If set, TLS handshake hostname when forwarding requests to the `TARGET`. If set to auto, use Host header. |
@@ -203,6 +203,7 @@ To get Anubis filtering your traffic, you need to make sure it's added to your H
- [Kubernetes](./environments/kubernetes.mdx)
- [Nginx](./environments/nginx.mdx)
- [Traefik](./environments/traefik.mdx)
+- [HAProxy](./environments/haproxy.mdx)
:::note
diff --git a/docs/docs/admin/native-install.mdx b/docs/docs/admin/native-install.mdx
index bdda5954..9abafa91 100644
--- a/docs/docs/admin/native-install.mdx
+++ b/docs/docs/admin/native-install.mdx
@@ -143,3 +143,4 @@ For more details on particular reverse proxies, see here:
- [Apache](./environments/apache.mdx)
- [Nginx](./environments/nginx.mdx)
+- [HAProxy](./environments/haproxy.mdx)
diff --git a/docs/docs/admin/policies.mdx b/docs/docs/admin/policies.mdx
index 159d1abc..24317a41 100644
--- a/docs/docs/admin/policies.mdx
+++ b/docs/docs/admin/policies.mdx
@@ -393,6 +393,32 @@ logging:
When files are rotated out, the old files will be named after the rotation timestamp in [RFC 3339 format](https://www.rfc-editor.org/rfc/rfc3339).
+:::note
+
+If you are running Anubis in systemd via a native package, the default systemd unit settings are very restrictive and will forbid writing to folders in `/var/log`. In order to fix this, please make a [drop-in unit](https://www.flatcar.org/docs/latest/setup/systemd/drop-in-units/) like the following:
+
+```text
+# /etc/systemd/anubis@instance-name.service.d/50-var-log-readwrite.conf
+[Service]
+ReadWritePaths=/run /var/log/anubis
+```
+
+Once you write this to the correct place, reload the systemd configuration:
+
+```text
+sudo systemctl daemon-reload
+```
+
+And then restart Anubis:
+
+```text
+sudo systemctl restart anubis@instance-name
+```
+
+You may be required to make drop-ins for each Anubis instance depending on the facts and circumstances of your deployment.
+
+:::
+
### `stdio` sink
By default, Anubis logs everything to the standard error stream of its process. This requires no configuration:
diff --git a/docs/docs/admin/roles/_category_.json b/docs/docs/admin/roles/_category_.json
index 2d5af0b3..d087404d 100644
--- a/docs/docs/admin/roles/_category_.json
+++ b/docs/docs/admin/roles/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Various server roles you will need to keep in mind with Anubis."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/design/_category_.json b/docs/docs/design/_category_.json
index 65764e85..87b1010e 100644
--- a/docs/docs/design/_category_.json
+++ b/docs/docs/design/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "How Anubis is designed and the tradeoffs it makes."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/developer/_category_.json b/docs/docs/developer/_category_.json
index cf3805e0..0b24e3b0 100644
--- a/docs/docs/developer/_category_.json
+++ b/docs/docs/developer/_category_.json
@@ -5,4 +5,4 @@
"type": "generated-index",
"description": "Guides and suggestions to make Anubis development go smoothly for everyone."
}
-}
\ No newline at end of file
+}
diff --git a/docs/docs/developer/ai-coding-policy.md b/docs/docs/developer/ai-coding-policy.md
new file mode 100644
index 00000000..b53e5594
--- /dev/null
+++ b/docs/docs/developer/ai-coding-policy.md
@@ -0,0 +1,13 @@
+# AI Coding Policy
+
+At some level it would be nice to be able to have the following AI coding policy from an ideological standpoint:
+
+> Anubis does not accept code made primarily with the use of agentic AI tools such as Claude Code, Gemini CLI, GitHub Copilot, Zed, OpenCode, or any other similar tools. Please do not use them when contributing to this repo.
+
+However, I'd be in violation by doing this because I have knowingly committed minor bits of code to the Anubis repo that were generated by AI tools (mostly things for smoke tests).
+
+As such, Anubis is taking more of a centrist approach with regards to AI coding tools: regardless of what tool you use to make contributions to Anubis, when you sign off your code, you are taking responsibility for what you commit. You are also expected to understand what you are changing, what the implications are, and all other relevant factors.
+
+If you use AI coding tools for a majority of your committed work, you MUST disclose it with [the `Assisted-by` footer](https://xeiaso.net/notes/2025/assisted-by-footer/). The Anubis maintainers will be using tooling that looks for these footers and will prioritize scrutiny and level of attention appropriately.
+
+In order to ensure compliance with this policy, language has been placed in `AGENTS.md` and `CLAUDE.md` to entice AI coding tools to add these footers.
diff --git a/docs/docs/developer/code-quality.md b/docs/docs/developer/code-quality.md
deleted file mode 100644
index 6dcc44ab..00000000
--- a/docs/docs/developer/code-quality.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-title: Code quality guidelines
----
-
-When submitting code to Anubis, please take the time to consider the fact that this project is security software. If things go bad, bots can pummel sites into oblivion. This is not ideal for uptime.
-
-As such, code reviews will be a bit more strict than you have seen in other projects. This is not people trying to be mean, this is a side effect of taking the problem seriously.
-
-When making code changes, try to do the following:
-
-- If you're submitting a bugfix, add a test case for it
-- If you're changing the JavaScript, make sure the integration tests pass (`npm run test:integration`)
-
-## Commit messages
-
-Anubis follows the Go project's conventions for commit messages. In general, an ideal commit message should read like this:
-
-```text
-path/to/folder: brief description of the change
-
-If the change is subtle, has implementation consequences, or is otherwise
-not entirely self-describing: take the time to spell out why. If things
-are very subtle, please also amend the documentation accordingly
-```
-
-The subject of a commit message should be the second half of the sentence "This commit changes the Anubis project to:". Here's a few examples:
-
-- `disable DroneBL by default`
-- `port the challenge to WebAssembly`
-
-The extended commit message is also your place to give rationale for a new feature. When maintainers are reviewing your code, they will use this to figure out if the burden from feature maintainership is worth the merge.
diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx
index 9898b85a..9db0a498 100644
--- a/docs/docs/index.mdx
+++ b/docs/docs/index.mdx
@@ -35,9 +35,21 @@ Anubis is brought to you by sponsors and donors like:
### Gold Tier
+
+
+
+
+
+
+
+
+
+
+
+
+-
+ Dolphin Emulator
+ - https://forums.dolphin-emu.org/
+ - https://wiki.dolphin-emu.org/
+
-
Duke University
- https://repository.duke.edu/
@@ -60,6 +63,11 @@ This page contains a non-exhaustive list with all websites using Anubis.
- https://find.library.duke.edu/
- https://nicholas.duke.edu/
+-
+ FFmpeg
+ - https://git.ffmpeg.org/
+ - https://trac.ffmpeg.org/
+
-
Forschungszentrum Jülich
- https://juser.fz-juelich.de/
@@ -112,11 +120,8 @@ This page contains a non-exhaustive list with all websites using Anubis.
- https://git.kernel.org/
- https://lore.kernel.org/
--
- The United Nations
- - https://policytoolbox.iiep.unesco.org/
-
-
Valve Corporation
- https://developer.valvesoftware.com/wiki/Main_Page
+ - https://wiki.teamfortress.com/wiki/Main_Page
diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts
index df12939b..e686522f 100644
--- a/docs/docusaurus.config.ts
+++ b/docs/docusaurus.config.ts
@@ -1,62 +1,62 @@
-import { themes as prismThemes } from 'prism-react-renderer';
-import type { Config } from '@docusaurus/types';
-import type * as Preset from '@docusaurus/preset-classic';
+import { themes as prismThemes } from "prism-react-renderer";
+import type { Config } from "@docusaurus/types";
+import type * as Preset from "@docusaurus/preset-classic";
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
const config: Config = {
- title: 'Anubis',
- tagline: 'Weigh the soul of incoming HTTP requests to protect your website!',
- favicon: 'img/favicon.ico',
+ title: "Anubis",
+ tagline: "Weigh the soul of incoming HTTP requests to protect your website!",
+ favicon: "img/favicon.ico",
// Set the production url of your site here
- url: 'https://anubis.techaro.lol',
+ url: "https://anubis.techaro.lol",
// Set the // pathname under which your site is served
// For GitHub pages deployment, it is often '//'
- baseUrl: '/',
+ baseUrl: "/",
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
- organizationName: 'TecharoHQ', // Usually your GitHub org/user name.
- projectName: 'anubis', // Usually your repo name.
+ organizationName: "TecharoHQ", // Usually your GitHub org/user name.
+ projectName: "anubis", // Usually your repo name.
- onBrokenLinks: 'throw',
- onBrokenMarkdownLinks: 'warn',
+ onBrokenLinks: "throw",
+ onBrokenMarkdownLinks: "warn",
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
- defaultLocale: 'en',
- locales: ['en'],
+ defaultLocale: "en",
+ locales: ["en"],
},
markdown: {
mermaid: true,
},
- themes: ['@docusaurus/theme-mermaid'],
+ themes: ["@docusaurus/theme-mermaid"],
presets: [
[
- 'classic',
+ "classic",
{
blog: {
showReadingTime: true,
feedOptions: {
- type: ['rss', 'atom', "json"],
+ type: ["rss", "atom", "json"],
xslt: true,
},
- editUrl: 'https://github.com/TecharoHQ/anubis/tree/main/docs/',
- onInlineTags: 'warn',
- onInlineAuthors: 'warn',
- onUntruncatedBlogPosts: 'throw',
+ editUrl: "https://github.com/TecharoHQ/anubis/tree/main/docs/",
+ onInlineTags: "warn",
+ onInlineAuthors: "warn",
+ onUntruncatedBlogPosts: "throw",
},
docs: {
- sidebarPath: './sidebars.ts',
- editUrl: 'https://github.com/TecharoHQ/anubis/tree/main/docs/',
+ sidebarPath: "./sidebars.ts",
+ editUrl: "https://github.com/TecharoHQ/anubis/tree/main/docs/",
},
theme: {
- customCss: './src/css/custom.css',
+ customCss: "./src/css/custom.css",
},
} satisfies Preset.Options,
],
@@ -67,47 +67,47 @@ const config: Config = {
respectPrefersColorScheme: true,
},
// Replace with your project's social card
- image: 'img/social-card.jpg',
+ image: "img/social-card.jpg",
navbar: {
- title: 'Anubis',
+ title: "Anubis",
logo: {
- alt: 'A happy jackal woman with brown hair and red eyes',
- src: 'img/favicon.webp',
+ alt: "A happy jackal woman with brown hair and red eyes",
+ src: "img/favicon.webp",
},
items: [
- { to: '/blog', label: 'Blog', position: 'left' },
+ { to: "/blog", label: "Blog", position: "left" },
{
- type: 'docSidebar',
- sidebarId: 'tutorialSidebar',
- position: 'left',
- label: 'Docs',
+ type: "docSidebar",
+ sidebarId: "tutorialSidebar",
+ position: "left",
+ label: "Docs",
},
{
- to: '/docs/admin/botstopper',
+ to: "/docs/admin/botstopper",
label: "Unbranded Version",
- position: "left"
+ position: "left",
},
{
- href: 'https://github.com/TecharoHQ/anubis',
- label: 'GitHub',
- position: 'right',
+ href: "https://github.com/TecharoHQ/anubis",
+ label: "GitHub",
+ position: "right",
},
{
- href: 'https://github.com/sponsors/Xe',
+ href: "https://github.com/sponsors/Xe",
label: "Sponsor the Project",
- position: 'right'
+ position: "right",
},
],
},
footer: {
- style: 'dark',
+ style: "dark",
links: [
{
- title: 'Docs',
+ title: "Docs",
items: [
{
- label: 'Intro',
- to: '/docs/',
+ label: "Intro",
+ to: "/docs/",
},
{
label: "Installation",
@@ -116,32 +116,32 @@ const config: Config = {
],
},
{
- title: 'Community',
+ title: "Community",
items: [
{
- label: 'GitHub Discussions',
- href: 'https://github.com/TecharoHQ/anubis/discussions',
+ label: "GitHub Discussions",
+ href: "https://github.com/TecharoHQ/anubis/discussions",
},
{
- label: 'Bluesky',
- href: 'https://bsky.app/profile/techaro.lol',
+ label: "Bluesky",
+ href: "https://bsky.app/profile/techaro.lol",
},
],
},
{
- title: 'More',
+ title: "More",
items: [
{
- label: 'Blog',
- to: '/blog',
+ label: "Blog",
+ to: "/blog",
},
{
- label: 'GitHub',
- href: 'https://github.com/TecharoHQ/anubis',
+ label: "GitHub",
+ href: "https://github.com/TecharoHQ/anubis",
},
{
- label: 'Status',
- href: 'https://techarohq.github.io/status/'
+ label: "Status",
+ href: "https://techarohq.github.io/status/",
},
],
},
@@ -153,13 +153,13 @@ const config: Config = {
darkTheme: prismThemes.dracula,
magicComments: [
{
- className: 'code-block-diff-add-line',
- line: 'diff-add'
+ className: "code-block-diff-add-line",
+ line: "diff-add",
},
{
- className: 'code-block-diff-remove-line',
- line: 'diff-remove'
- }
+ className: "code-block-diff-remove-line",
+ line: "diff-remove",
+ },
],
},
} satisfies Preset.ThemeConfig,
diff --git a/docs/manifest/ingress.yaml b/docs/manifest/ingress.yaml
index 52478361..8ce3b9fc 100644
--- a/docs/manifest/ingress.yaml
+++ b/docs/manifest/ingress.yaml
@@ -21,4 +21,4 @@ spec:
service:
name: anubis-docs
port:
- name: anubis
\ No newline at end of file
+ name: anubis
diff --git a/docs/manifest/onionservice.yaml b/docs/manifest/onionservice.yaml
index 81ab9729..e0584468 100644
--- a/docs/manifest/onionservice.yaml
+++ b/docs/manifest/onionservice.yaml
@@ -5,10 +5,10 @@ metadata:
spec:
version: 3
rules:
- - port:
- number: 80
- backend:
- service:
- name: anubis-docs
- port:
- number: 80
\ No newline at end of file
+ - port:
+ number: 80
+ backend:
+ service:
+ name: anubis-docs
+ port:
+ number: 80
diff --git a/docs/manifest/service.yaml b/docs/manifest/service.yaml
index dadac058..1acdc45d 100644
--- a/docs/manifest/service.yaml
+++ b/docs/manifest/service.yaml
@@ -6,9 +6,9 @@ spec:
selector:
app: anubis-docs
ports:
- - port: 80
- targetPort: 80
- name: http
- - port: 8081
- targetPort: 8081
- name: anubis
+ - port: 80
+ targetPort: 80
+ name: http
+ - port: 8081
+ targetPort: 8081
+ name: anubis
diff --git a/docs/sidebars.ts b/docs/sidebars.ts
index 28971397..574ed394 100644
--- a/docs/sidebars.ts
+++ b/docs/sidebars.ts
@@ -1,4 +1,4 @@
-import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
+import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
@@ -14,7 +14,7 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
*/
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
- tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
+ tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
// But you can create a sidebar manually
/*
diff --git a/docs/src/components/EnterpriseOnly/index.jsx b/docs/src/components/EnterpriseOnly/index.jsx
index bfd9486c..ac6bab1d 100644
--- a/docs/src/components/EnterpriseOnly/index.jsx
+++ b/docs/src/components/EnterpriseOnly/index.jsx
@@ -1,4 +1,4 @@
-import styles from './styles.module.css';
+import styles from "./styles.module.css";
export default function EnterpriseOnly({ link }) {
return (
@@ -8,4 +8,4 @@ export default function EnterpriseOnly({ link }) {
);
-}
\ No newline at end of file
+}
diff --git a/docs/src/components/EnterpriseOnly/styles.module.css b/docs/src/components/EnterpriseOnly/styles.module.css
index f3ac7a15..056cedc0 100644
--- a/docs/src/components/EnterpriseOnly/styles.module.css
+++ b/docs/src/components/EnterpriseOnly/styles.module.css
@@ -8,7 +8,9 @@
font-weight: 700;
padding: 0.5rem 1rem; /* py-2 px-4 */
border-radius: 9999px; /* rounded-full */
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-lg approximation */
+ box-shadow:
+ 0 10px 15px -3px rgba(0, 0, 0, 0.1),
+ 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-lg approximation */
display: inline-flex; /* flex */
align-items: center; /* items-center */
}
diff --git a/docs/static/img/sponsors/anexia-cloudsolutions-logo.webp b/docs/static/img/sponsors/anexia-cloudsolutions-logo.webp
new file mode 100644
index 00000000..3358e41b
Binary files /dev/null and b/docs/static/img/sponsors/anexia-cloudsolutions-logo.webp differ
diff --git a/docs/static/img/sponsors/gitea-logo.webp b/docs/static/img/sponsors/gitea-logo.webp
new file mode 100644
index 00000000..97e0e8fe
Binary files /dev/null and b/docs/static/img/sponsors/gitea-logo.webp differ
diff --git a/docs/static/img/sponsors/prolocation-logo.svg b/docs/static/img/sponsors/prolocation-logo.svg
new file mode 100644
index 00000000..43ffc0bf
--- /dev/null
+++ b/docs/static/img/sponsors/prolocation-logo.svg
@@ -0,0 +1,37 @@
+
+
+
+
diff --git a/docs/static/img/sponsors/unipromos.webp b/docs/static/img/sponsors/unipromos.webp
new file mode 100644
index 00000000..c0a0213e
Binary files /dev/null and b/docs/static/img/sponsors/unipromos.webp differ
diff --git a/docs/static/img/sponsors/uvensys.webp b/docs/static/img/sponsors/uvensys.webp
new file mode 100644
index 00000000..aaa641f9
Binary files /dev/null and b/docs/static/img/sponsors/uvensys.webp differ
diff --git a/internal/glob/glob.go b/internal/glob/glob.go
index 44c1a67a..e2e640de 100644
--- a/internal/glob/glob.go
+++ b/internal/glob/glob.go
@@ -36,7 +36,7 @@ func Glob(pattern, subj string) bool {
end := len(parts) - 1
// Go over the leading parts and ensure they match.
- for i := 0; i < end; i++ {
+ for i := range end {
idx := strings.Index(subj, parts[i])
switch i {
diff --git a/internal/hash_bench_test.go b/internal/hash_bench_test.go
index 5384570a..88f87e7c 100644
--- a/internal/hash_bench_test.go
+++ b/internal/hash_bench_test.go
@@ -184,7 +184,7 @@ func TestHashCollisions(t *testing.T) {
for _, prefix := range prefixes {
for _, suffix := range suffixes {
for _, variation := range variations {
- for i := 0; i < 100; i++ {
+ for i := range 100 {
input := fmt.Sprintf("%s%s%s-%d", prefix, suffix, variation, i)
hash := XXHash64sum(input)
if existing, exists := xxhashHashes[hash]; exists {
@@ -211,7 +211,7 @@ func TestHashCollisions(t *testing.T) {
seqCount := 0
for _, pattern := range patterns {
- for i := 0; i < 10000; i++ {
+ for i := range 10000 {
input := fmt.Sprintf(pattern, i)
hash := XXHash64sum(input)
if existing, exists := xxhashHashes[hash]; exists {
diff --git a/internal/honeypot/naive/naive.go b/internal/honeypot/naive/naive.go
index f62f4ef8..95093bcf 100644
--- a/internal/honeypot/naive/naive.go
+++ b/internal/honeypot/naive/naive.go
@@ -120,7 +120,7 @@ func (i *Impl) makeAffirmations() []string {
count := rand.IntN(5) + 1
var result []string
- for j := 0; j < count; j++ {
+ for range count {
result = append(result, i.affirmation.Spin())
}
@@ -131,7 +131,7 @@ func (i *Impl) makeSpins() []string {
count := rand.IntN(5) + 1
var result []string
- for j := 0; j < count; j++ {
+ for range count {
result = append(result, i.body.Spin())
}
diff --git a/internal/listor.go b/internal/listor.go
index b6ba57fe..73110b79 100644
--- a/internal/listor.go
+++ b/internal/listor.go
@@ -16,7 +16,7 @@ func (lo *ListOr[T]) UnmarshalJSON(data []byte) error {
// Check if first non-whitespace character is '['
firstChar := data[0]
- for i := 0; i < len(data); i++ {
+ for i := range data {
if data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' {
firstChar = data[i]
break
@@ -36,4 +36,4 @@ func (lo *ListOr[T]) UnmarshalJSON(data []byte) error {
}
return nil
-}
\ No newline at end of file
+}
diff --git a/internal/ogtags/mem_test.go b/internal/ogtags/mem_test.go
index 3770a73f..aebb4f6f 100644
--- a/internal/ogtags/mem_test.go
+++ b/internal/ogtags/mem_test.go
@@ -95,7 +95,7 @@ func TestMemoryUsage(t *testing.T) {
// Run getTarget many times
u, _ := url.Parse("/path/to/resource?query=1&foo=bar&baz=qux")
- for i := 0; i < 10000; i++ {
+ for range 10000 {
_ = cache.getTarget(u)
}
@@ -129,7 +129,7 @@ func TestMemoryUsage(t *testing.T) {
runtime.GC()
runtime.ReadMemStats(&m1)
- for i := 0; i < 1000; i++ {
+ for range 1000 {
_ = cache.extractOGTags(doc)
}
diff --git a/internal/ogtags/ogtags_fuzz_test.go b/internal/ogtags/ogtags_fuzz_test.go
index 6355eebf..8b7abb91 100644
--- a/internal/ogtags/ogtags_fuzz_test.go
+++ b/internal/ogtags/ogtags_fuzz_test.go
@@ -3,6 +3,7 @@ package ogtags
import (
"context"
"net/url"
+ "slices"
"strings"
"testing"
"unicode/utf8"
@@ -78,7 +79,7 @@ func FuzzGetTarget(f *testing.F) {
}
// Ensure no memory corruption by calling multiple times
- for i := 0; i < 3; i++ {
+ for range 3 {
result2 := cache.getTarget(u)
if result != result2 {
t.Errorf("getTarget not deterministic: %q != %q", result, result2)
@@ -148,11 +149,8 @@ func FuzzExtractOGTags(f *testing.F) {
}
}
if !approved {
- for _, tag := range cache.approvedTags {
- if property == tag {
- approved = true
- break
- }
+ if slices.Contains(cache.approvedTags, property) {
+ approved = true
}
}
if !approved {
@@ -260,11 +258,8 @@ func FuzzExtractMetaTagInfo(f *testing.F) {
}
}
if !approved {
- for _, tag := range cache.approvedTags {
- if property == tag {
- approved = true
- break
- }
+ if slices.Contains(cache.approvedTags, property) {
+ approved = true
}
}
if !approved {
diff --git a/internal/ogtags/parse.go b/internal/ogtags/parse.go
index c21fd795..98176558 100644
--- a/internal/ogtags/parse.go
+++ b/internal/ogtags/parse.go
@@ -1,6 +1,7 @@
package ogtags
import (
+ "slices"
"strings"
"golang.org/x/net/html"
@@ -65,10 +66,8 @@ func (c *OGTagCache) extractMetaTagInfo(n *html.Node) (property, content string)
}
// Check exact matches
- for _, tag := range c.approvedTags {
- if propertyKey == tag {
- return propertyKey, content
- }
+ if slices.Contains(c.approvedTags, propertyKey) {
+ return propertyKey, content
}
return "", content
diff --git a/internal/test/playwright_test.go b/internal/test/playwright_test.go
index b1cab340..4d6355ba 100644
--- a/internal/test/playwright_test.go
+++ b/internal/test/playwright_test.go
@@ -270,7 +270,7 @@ func TestPlaywrightBrowser(t *testing.T) {
var performedAction action
var err error
- for i := 0; i < 5; i++ {
+ for i := range 5 {
performedAction, err = executeTestCase(t, tc, typ, anubisURL)
if performedAction == tc.action {
break
diff --git a/lib/anubis.go b/lib/anubis.go
index feff53a3..2ff5ec92 100644
--- a/lib/anubis.go
+++ b/lib/anubis.go
@@ -81,11 +81,11 @@ type Server struct {
func (s *Server) getTokenKeyfunc() jwt.Keyfunc {
// return ED25519 key if HS512 is not set
if len(s.hs512Secret) == 0 {
- return func(token *jwt.Token) (interface{}, error) {
+ return func(token *jwt.Token) (any, error) {
return s.ed25519Priv.Public().(ed25519.PublicKey), nil
}
} else {
- return func(token *jwt.Token) (interface{}, error) {
+ return func(token *jwt.Token) (any, error) {
return s.hs512Secret, nil
}
}
@@ -106,6 +106,13 @@ func (s *Server) issueChallenge(ctx context.Context, r *http.Request, lg *slog.L
//return nil, errors.New("[unexpected] this codepath should be impossible, asked to issue a challenge for a non-challenge rule")
}
+ if rule.Challenge == nil {
+ rule.Challenge = &config.ChallengeRules{
+ Difficulty: s.policy.DefaultDifficulty,
+ Algorithm: config.DefaultAlgorithm,
+ }
+ }
+
id, err := uuid.NewV7()
if err != nil {
return nil, err
@@ -491,7 +498,11 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
chall, err := s.getChallenge(r)
if err != nil {
lg.Error("getChallenge failed", "err", err)
- s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err))
+ algorithm := "unknown"
+ if rule.Challenge != nil {
+ algorithm = rule.Challenge.Algorithm
+ }
+ s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), algorithm), makeCode(err))
return
}
@@ -638,8 +649,16 @@ func (s *Server) check(r *http.Request, lg *slog.Logger) (policy.CheckResult, *p
}
if matches {
+ challRules := t.Challenge
+ if challRules == nil {
+ // Non-CHALLENGE thresholds (ALLOW/DENY) don't have challenge config.
+ // Use an empty struct so hydrateChallengeRule can fill from stored
+ // challenge data during validation, rather than baking in defaults
+ // that could mismatch the difficulty the client actually solved for.
+ challRules = &config.ChallengeRules{}
+ }
return cr("threshold/"+t.Name, t.Action, weight), &policy.Bot{
- Challenge: t.Challenge,
+ Challenge: challRules,
Rules: &checker.List{},
}, nil
}
diff --git a/lib/anubis_test.go b/lib/anubis_test.go
index 07785d37..7c0f87d3 100644
--- a/lib/anubis_test.go
+++ b/lib/anubis_test.go
@@ -38,8 +38,8 @@ func NewTLogWriter(t *testing.T) io.Writer {
// Write splits input on newlines and logs each line separately.
func (w *TLogWriter) Write(p []byte) (n int, err error) {
- lines := strings.Split(string(p), "\n")
- for _, line := range lines {
+ lines := strings.SplitSeq(string(p), "\n")
+ for line := range lines {
if line != "" {
w.t.Log(line)
}
diff --git a/lib/challenge/challenge.go b/lib/challenge/challenge.go
index d0cfbf77..d075930a 100644
--- a/lib/challenge/challenge.go
+++ b/lib/challenge/challenge.go
@@ -4,12 +4,12 @@ import "time"
// Challenge is the metadata about a single challenge issuance.
type Challenge struct {
- IssuedAt time.Time `json:"issuedAt"`
- Metadata map[string]string `json:"metadata"`
- ID string `json:"id"`
- Method string `json:"method"`
- RandomData string `json:"randomData"`
- PolicyRuleHash string `json:"policyRuleHash,omitempty"`
- Difficulty int `json:"difficulty,omitempty"`
- Spent bool `json:"spent"`
+ IssuedAt time.Time `json:"issuedAt"` // When the challenge was issued
+ Metadata map[string]string `json:"metadata"` // Challenge metadata such as IP address and user agent
+ ID string `json:"id"` // UUID identifying the challenge
+ Method string `json:"method"` // Challenge method
+ RandomData string `json:"randomData"` // The random data the client processes
+ PolicyRuleHash string `json:"policyRuleHash,omitempty"` // Hash of the policy rule that issued this challenge
+ Difficulty int `json:"difficulty,omitempty"` // Difficulty that was in effect when issued
+ Spent bool `json:"spent"` // Has the challenge already been solved?
}
diff --git a/lib/challenge/error.go b/lib/challenge/error.go
index 7a9d3094..8c28a1c6 100644
--- a/lib/challenge/error.go
+++ b/lib/challenge/error.go
@@ -10,6 +10,7 @@ var (
ErrFailed = errors.New("challenge: user failed challenge")
ErrMissingField = errors.New("challenge: missing field")
ErrInvalidFormat = errors.New("challenge: field has invalid format")
+ ErrInvalidInput = errors.New("challenge: input is nil or missing required fields")
)
func NewError(verb, publicReason string, privateReason error) *Error {
diff --git a/lib/challenge/interface.go b/lib/challenge/interface.go
index 4bef2e7f..e6d64fd2 100644
--- a/lib/challenge/interface.go
+++ b/lib/challenge/interface.go
@@ -1,6 +1,7 @@
package challenge
import (
+ "fmt"
"log/slog"
"net/http"
"sort"
@@ -50,12 +51,44 @@ type IssueInput struct {
Store store.Interface
}
+func (in *IssueInput) Valid() error {
+ if in == nil {
+ return fmt.Errorf("%w: IssueInput is nil", ErrInvalidInput)
+ }
+ if in.Rule == nil {
+ return fmt.Errorf("%w: Rule is nil", ErrInvalidInput)
+ }
+ if in.Rule.Challenge == nil {
+ return fmt.Errorf("%w: Rule.Challenge is nil", ErrInvalidInput)
+ }
+ if in.Challenge == nil {
+ return fmt.Errorf("%w: Challenge is nil", ErrInvalidInput)
+ }
+ return nil
+}
+
type ValidateInput struct {
Rule *policy.Bot
Challenge *Challenge
Store store.Interface
}
+func (in *ValidateInput) Valid() error {
+ if in == nil {
+ return fmt.Errorf("%w: ValidateInput is nil", ErrInvalidInput)
+ }
+ if in.Rule == nil {
+ return fmt.Errorf("%w: Rule is nil", ErrInvalidInput)
+ }
+ if in.Rule.Challenge == nil {
+ return fmt.Errorf("%w: Rule.Challenge is nil", ErrInvalidInput)
+ }
+ if in.Challenge == nil {
+ return fmt.Errorf("%w: Challenge is nil", ErrInvalidInput)
+ }
+ return nil
+}
+
type Impl interface {
// Setup registers any additional routes with the Impl for assets or API routes.
Setup(mux *http.ServeMux)
diff --git a/lib/challenge/metarefresh/metarefresh.go b/lib/challenge/metarefresh/metarefresh.go
index cb5b023c..cb8dbf8c 100644
--- a/lib/challenge/metarefresh/metarefresh.go
+++ b/lib/challenge/metarefresh/metarefresh.go
@@ -24,6 +24,10 @@ type Impl struct{}
func (i *Impl) Setup(mux *http.ServeMux) {}
func (i *Impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *challenge.IssueInput) (templ.Component, error) {
+ if err := in.Valid(); err != nil {
+ return nil, err
+ }
+
u, err := r.URL.Parse(anubis.BasePrefix + "/.within.website/x/cmd/anubis/api/pass-challenge")
if err != nil {
return nil, fmt.Errorf("can't render page: %w", err)
@@ -49,6 +53,10 @@ func (i *Impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in
}
func (i *Impl) Validate(r *http.Request, lg *slog.Logger, in *challenge.ValidateInput) error {
+ if err := in.Valid(); err != nil {
+ return challenge.NewError("validate", "invalid input", err)
+ }
+
wantTime := in.Challenge.IssuedAt.Add(time.Duration(in.Rule.Challenge.Difficulty) * 800 * time.Millisecond)
if time.Now().Before(wantTime) {
diff --git a/lib/challenge/preact/js/xeact.js b/lib/challenge/preact/js/xeact.js
index cf4a102e..1488f8b8 100644
--- a/lib/challenge/preact/js/xeact.js
+++ b/lib/challenge/preact/js/xeact.js
@@ -5,7 +5,9 @@
*/
const h = (name, data = {}, children = []) => {
const result =
- typeof name == "function" ? name(data) : Object.assign(document.createElement(name), data);
+ typeof name == "function"
+ ? name(data)
+ : Object.assign(document.createElement(name), data);
if (!Array.isArray(children)) {
children = [children];
}
@@ -118,10 +120,10 @@ const d = (ms) => {
/**
* Parse the contents of a given HTML page element as JSON and
* return the results.
- *
+ *
* This is useful when using templ to pass complicated data from
* the server to the client via HTML[1].
- *
+ *
* [1]: https://templ.guide/syntax-and-usage/script-templates/#pass-server-side-data-to-the-client-in-a-html-attribute
*/
const j = (id) => JSON.parse(g(id).textContent);
diff --git a/lib/challenge/preact/preact.go b/lib/challenge/preact/preact.go
index 896642e2..e39cd4d8 100644
--- a/lib/challenge/preact/preact.go
+++ b/lib/challenge/preact/preact.go
@@ -39,6 +39,10 @@ type impl struct{}
func (i *impl) Setup(mux *http.ServeMux) {}
func (i *impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *challenge.IssueInput) (templ.Component, error) {
+ if err := in.Valid(); err != nil {
+ return nil, err
+ }
+
u, err := r.URL.Parse(anubis.BasePrefix + "/.within.website/x/cmd/anubis/api/pass-challenge")
if err != nil {
return nil, fmt.Errorf("can't render page: %w", err)
@@ -57,6 +61,10 @@ func (i *impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in
}
func (i *impl) Validate(r *http.Request, lg *slog.Logger, in *challenge.ValidateInput) error {
+ if err := in.Valid(); err != nil {
+ return challenge.NewError("validate", "invalid input", err)
+ }
+
wantTime := in.Challenge.IssuedAt.Add(time.Duration(in.Rule.Challenge.Difficulty) * 80 * time.Millisecond)
if time.Now().Before(wantTime) {
diff --git a/lib/challenge/proofofwork/proofofwork.go b/lib/challenge/proofofwork/proofofwork.go
index b9be014e..3e4bce3b 100644
--- a/lib/challenge/proofofwork/proofofwork.go
+++ b/lib/challenge/proofofwork/proofofwork.go
@@ -33,6 +33,10 @@ func (i *Impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in
}
func (i *Impl) Validate(r *http.Request, lg *slog.Logger, in *chall.ValidateInput) error {
+ if err := in.Valid(); err != nil {
+ return chall.NewError("validate", "invalid input", err)
+ }
+
rule := in.Rule
challenge := in.Challenge.RandomData
diff --git a/lib/challenge/proofofwork/proofofwork_test.go b/lib/challenge/proofofwork/proofofwork_test.go
index 069636bd..f5d6dfb7 100644
--- a/lib/challenge/proofofwork/proofofwork_test.go
+++ b/lib/challenge/proofofwork/proofofwork_test.go
@@ -30,6 +30,62 @@ func mkRequest(t *testing.T, values map[string]string) *http.Request {
return req
}
+// TestValidateNilRuleChallenge reproduces the panic from
+// https://github.com/TecharoHQ/anubis/issues/1463
+//
+// When a threshold rule matches during PassChallenge, check() can return
+// a policy.Bot with Challenge == nil. After hydrateChallengeRule fails to
+// run (or the error path hits before it), Validate dereferences
+// rule.Challenge.Difficulty and panics.
+func TestValidateNilRuleChallenge(t *testing.T) {
+ i := &Impl{Algorithm: "fast"}
+ lg := slog.With()
+
+ // This is the exact response for SHA256("hunter" + "0") with 0 leading zeros required.
+ const challengeStr = "hunter"
+ const response = "2652bdba8fb4d2ab39ef28d8534d7694c557a4ae146c1e9237bd8d950280500e"
+
+ req := mkRequest(t, map[string]string{
+ "nonce": "0",
+ "elapsedTime": "69",
+ "response": response,
+ })
+
+ for _, tc := range []struct {
+ name string
+ input *challenge.ValidateInput
+ }{
+ {
+ name: "nil-rule-challenge",
+ input: &challenge.ValidateInput{
+ Rule: &policy.Bot{},
+ Challenge: &challenge.Challenge{RandomData: challengeStr},
+ },
+ },
+ {
+ name: "nil-rule",
+ input: &challenge.ValidateInput{
+ Challenge: &challenge.Challenge{RandomData: challengeStr},
+ },
+ },
+ {
+ name: "nil-challenge",
+ input: &challenge.ValidateInput{Rule: &policy.Bot{Challenge: &config.ChallengeRules{Algorithm: "fast"}}},
+ },
+ {
+ name: "nil-input",
+ input: nil,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ err := i.Validate(req, lg, tc.input)
+ if !errors.Is(err, challenge.ErrInvalidInput) {
+ t.Fatalf("expected ErrInvalidInput, got: %v", err)
+ }
+ })
+ }
+}
+
func TestBasic(t *testing.T) {
i := &Impl{Algorithm: "fast"}
bot := &policy.Bot{
diff --git a/lib/config/config.go b/lib/config/config.go
index d50cdae3..01b46576 100644
--- a/lib/config/config.go
+++ b/lib/config/config.go
@@ -228,8 +228,8 @@ type ImportStatement struct {
}
func (is *ImportStatement) open() (fs.File, error) {
- if strings.HasPrefix(is.Import, "(data)/") {
- fname := strings.TrimPrefix(is.Import, "(data)/")
+ if after, ok := strings.CutPrefix(is.Import, "(data)/"); ok {
+ fname := after
fin, err := data.BotPolicies.Open(fname)
return fin, err
}
@@ -325,7 +325,7 @@ func (sc StatusCodes) Valid() error {
}
type fileConfig struct {
- OpenGraph openGraphFileConfig `json:"openGraph,omitempty"`
+ OpenGraph openGraphFileConfig `json:"openGraph"`
Impressum *Impressum `json:"impressum,omitempty"`
Store *Store `json:"store"`
Bots []BotOrImport `json:"bots"`
diff --git a/lib/config/config_test.go b/lib/config/config_test.go
index 1b933759..ce57feed 100644
--- a/lib/config/config_test.go
+++ b/lib/config/config_test.go
@@ -188,7 +188,6 @@ func TestBotValid(t *testing.T) {
}
for _, cs := range tests {
- cs := cs
t.Run(cs.name, func(t *testing.T) {
err := cs.bot.Valid()
if err == nil && cs.err == nil {
@@ -216,7 +215,6 @@ func TestConfigValidKnownGood(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
fin, err := os.Open(filepath.Join("testdata", "good", st.Name()))
if err != nil {
@@ -303,7 +301,6 @@ func TestConfigValidBad(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
fin, err := os.Open(filepath.Join("testdata", "bad", st.Name()))
if err != nil {
diff --git a/lib/config/testdata/bad/badregexes.json b/lib/config/testdata/bad/badregexes.json
index db371b0f..436f768d 100644
--- a/lib/config/testdata/bad/badregexes.json
+++ b/lib/config/testdata/bad/badregexes.json
@@ -18,4 +18,4 @@
"action": "DENY"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/bad/badregexes.yaml b/lib/config/testdata/bad/badregexes.yaml
index 3880e407..c6caedbd 100644
--- a/lib/config/testdata/bad/badregexes.yaml
+++ b/lib/config/testdata/bad/badregexes.yaml
@@ -1,7 +1,7 @@
bots:
-- name: path-bad
- path_regex: "a(b"
- action: DENY
-- name: user-agent-bad
- user_agent_regex: "a(b"
- action: DENY
\ No newline at end of file
+ - name: path-bad
+ path_regex: "a(b"
+ action: DENY
+ - name: user-agent-bad
+ user_agent_regex: "a(b"
+ action: DENY
diff --git a/lib/config/testdata/bad/import_and_bot.json b/lib/config/testdata/bad/import_and_bot.json
index 3d0519bc..ef9778cd 100644
--- a/lib/config/testdata/bad/import_and_bot.json
+++ b/lib/config/testdata/bad/import_and_bot.json
@@ -7,4 +7,4 @@
"action": "CHALLENGE"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/bad/import_and_bot.yaml b/lib/config/testdata/bad/import_and_bot.yaml
index fdfaa43a..cc9142db 100644
--- a/lib/config/testdata/bad/import_and_bot.yaml
+++ b/lib/config/testdata/bad/import_and_bot.yaml
@@ -1,6 +1,6 @@
bots:
-- import: (data)/bots/ai-catchall.yaml
- name: generic-browser
- user_agent_regex: >
- Mozilla|Opera
- action: CHALLENGE
\ No newline at end of file
+ - import: (data)/bots/ai-catchall.yaml
+ name: generic-browser
+ user_agent_regex: >
+ Mozilla|Opera
+ action: CHALLENGE
diff --git a/lib/config/testdata/bad/import_invalid_file.json b/lib/config/testdata/bad/import_invalid_file.json
index c7546c09..26669635 100644
--- a/lib/config/testdata/bad/import_invalid_file.json
+++ b/lib/config/testdata/bad/import_invalid_file.json
@@ -4,4 +4,4 @@
"import": "(data)/does-not-exist-fake-file.yaml"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/bad/import_invalid_file.yaml b/lib/config/testdata/bad/import_invalid_file.yaml
index df78c067..c82b4533 100644
--- a/lib/config/testdata/bad/import_invalid_file.yaml
+++ b/lib/config/testdata/bad/import_invalid_file.yaml
@@ -1,2 +1,2 @@
bots:
-- import: (data)/does-not-exist-fake-file.yaml
\ No newline at end of file
+ - import: (data)/does-not-exist-fake-file.yaml
diff --git a/lib/config/testdata/bad/invalid.json b/lib/config/testdata/bad/invalid.json
index c5d1ff6c..334e5db7 100644
--- a/lib/config/testdata/bad/invalid.json
+++ b/lib/config/testdata/bad/invalid.json
@@ -1,5 +1,3 @@
{
- "bots": [
- {}
- ]
-}
\ No newline at end of file
+ "bots": [{}]
+}
diff --git a/lib/config/testdata/bad/invalid.yaml b/lib/config/testdata/bad/invalid.yaml
index 18625b61..b78eb920 100644
--- a/lib/config/testdata/bad/invalid.yaml
+++ b/lib/config/testdata/bad/invalid.yaml
@@ -1 +1 @@
-bots: []
\ No newline at end of file
+bots: []
diff --git a/lib/config/testdata/bad/multiple_expression_types.json b/lib/config/testdata/bad/multiple_expression_types.json
index 8b852768..0bdf5bfc 100644
--- a/lib/config/testdata/bad/multiple_expression_types.json
+++ b/lib/config/testdata/bad/multiple_expression_types.json
@@ -8,10 +8,8 @@
"userAgent.startsWith(\"git/\") || userAgent.contains(\"libgit\")",
"\"Git-Protocol\" in headers && headers[\"Git-Protocol\"] == \"version=2\"\n"
],
- "any": [
- "userAgent.startsWith(\"evilbot/\")"
- ]
+ "any": ["userAgent.startsWith(\"evilbot/\")"]
}
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/bad/multiple_expression_types.yaml b/lib/config/testdata/bad/multiple_expression_types.yaml
index f7aa5463..da97d454 100644
--- a/lib/config/testdata/bad/multiple_expression_types.yaml
+++ b/lib/config/testdata/bad/multiple_expression_types.yaml
@@ -1,10 +1,10 @@
bots:
-- name: multiple-expression-types
- action: ALLOW
- expression:
- all:
- - userAgent.startsWith("git/") || userAgent.contains("libgit")
- - >
- "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
- any:
- - userAgent.startsWith("evilbot/")
+ - name: multiple-expression-types
+ action: ALLOW
+ expression:
+ all:
+ - userAgent.startsWith("git/") || userAgent.contains("libgit")
+ - >
+ "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
+ any:
+ - userAgent.startsWith("evilbot/")
diff --git a/lib/config/testdata/bad/nobots.json b/lib/config/testdata/bad/nobots.json
index 9e26dfee..0967ef42 100644
--- a/lib/config/testdata/bad/nobots.json
+++ b/lib/config/testdata/bad/nobots.json
@@ -1 +1 @@
-{}
\ No newline at end of file
+{}
diff --git a/lib/config/testdata/bad/nobots.yaml b/lib/config/testdata/bad/nobots.yaml
index 9e26dfee..0967ef42 100644
--- a/lib/config/testdata/bad/nobots.yaml
+++ b/lib/config/testdata/bad/nobots.yaml
@@ -1 +1 @@
-{}
\ No newline at end of file
+{}
diff --git a/lib/config/testdata/bad/regex_ends_newline.yaml b/lib/config/testdata/bad/regex_ends_newline.yaml
index 1f0ae85b..7400016b 100644
--- a/lib/config/testdata/bad/regex_ends_newline.yaml
+++ b/lib/config/testdata/bad/regex_ends_newline.yaml
@@ -1,17 +1,17 @@
bots:
-- name: user-agent-ends-newline
- # Subtle bug: this ends with a newline
- user_agent_regex: >
- Mozilla
- action: CHALLENGE
-- name: path-ends-newline
- # Subtle bug: this ends with a newline
- path_regex: >
- ^/evil/.*$
- action: CHALLENGE
-- name: headers-ends-newline
- # Subtle bug: this ends with a newline
- headers_regex:
- CF-Worker: >
- .*
- action: CHALLENGE
\ No newline at end of file
+ - name: user-agent-ends-newline
+ # Subtle bug: this ends with a newline
+ user_agent_regex: >
+ Mozilla
+ action: CHALLENGE
+ - name: path-ends-newline
+ # Subtle bug: this ends with a newline
+ path_regex: >
+ ^/evil/.*$
+ action: CHALLENGE
+ - name: headers-ends-newline
+ # Subtle bug: this ends with a newline
+ headers_regex:
+ CF-Worker: >
+ .*
+ action: CHALLENGE
diff --git a/lib/config/testdata/bad/status-codes-0.yaml b/lib/config/testdata/bad/status-codes-0.yaml
index 0d08322e..96df3d1d 100644
--- a/lib/config/testdata/bad/status-codes-0.yaml
+++ b/lib/config/testdata/bad/status-codes-0.yaml
@@ -1,8 +1,8 @@
bots:
-- name: everything
- user_agent_regex: .*
- action: DENY
+ - name: everything
+ user_agent_regex: .*
+ action: DENY
status_codes:
CHALLENGE: 0
- DENY: 0
\ No newline at end of file
+ DENY: 0
diff --git a/lib/config/testdata/good/allow_everyone.json b/lib/config/testdata/good/allow_everyone.json
index a7e1af76..de298c6b 100644
--- a/lib/config/testdata/good/allow_everyone.json
+++ b/lib/config/testdata/good/allow_everyone.json
@@ -2,11 +2,8 @@
"bots": [
{
"name": "everyones-invited",
- "remote_addresses": [
- "0.0.0.0/0",
- "::/0"
- ],
+ "remote_addresses": ["0.0.0.0/0", "::/0"],
"action": "ALLOW"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/allow_everyone.yaml b/lib/config/testdata/good/allow_everyone.yaml
index 5c495345..80dae8b2 100644
--- a/lib/config/testdata/good/allow_everyone.yaml
+++ b/lib/config/testdata/good/allow_everyone.yaml
@@ -1,6 +1,6 @@
bots:
-- name: everyones-invited
- remote_addresses:
- - "0.0.0.0/0"
- - "::/0"
- action: ALLOW
\ No newline at end of file
+ - name: everyones-invited
+ remote_addresses:
+ - "0.0.0.0/0"
+ - "::/0"
+ action: ALLOW
diff --git a/lib/config/testdata/good/block_cf_workers.json b/lib/config/testdata/good/block_cf_workers.json
index b84f1e0d..380fdf04 100644
--- a/lib/config/testdata/good/block_cf_workers.json
+++ b/lib/config/testdata/good/block_cf_workers.json
@@ -9,4 +9,4 @@
}
],
"dnsbl": false
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/block_cf_workers.yaml b/lib/config/testdata/good/block_cf_workers.yaml
index c66badef..353c921a 100644
--- a/lib/config/testdata/good/block_cf_workers.yaml
+++ b/lib/config/testdata/good/block_cf_workers.yaml
@@ -2,4 +2,4 @@ bots:
- name: cloudflare-workers
headers_regex:
CF-Worker: .*
- action: DENY
\ No newline at end of file
+ action: DENY
diff --git a/lib/config/testdata/good/challengemozilla.json b/lib/config/testdata/good/challengemozilla.json
index e9d34eeb..eaee8490 100644
--- a/lib/config/testdata/good/challengemozilla.json
+++ b/lib/config/testdata/good/challengemozilla.json
@@ -6,4 +6,4 @@
"action": "CHALLENGE"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/challengemozilla.yaml b/lib/config/testdata/good/challengemozilla.yaml
index 15922b0a..f6d8e9ac 100644
--- a/lib/config/testdata/good/challengemozilla.yaml
+++ b/lib/config/testdata/good/challengemozilla.yaml
@@ -1,4 +1,4 @@
bots:
-- name: generic-browser
- user_agent_regex: Mozilla
- action: CHALLENGE
\ No newline at end of file
+ - name: generic-browser
+ user_agent_regex: Mozilla
+ action: CHALLENGE
diff --git a/lib/config/testdata/good/everything_blocked.json b/lib/config/testdata/good/everything_blocked.json
index e1763e45..ab694f25 100644
--- a/lib/config/testdata/good/everything_blocked.json
+++ b/lib/config/testdata/good/everything_blocked.json
@@ -7,4 +7,4 @@
}
],
"dnsbl": false
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/everything_blocked.yaml b/lib/config/testdata/good/everything_blocked.yaml
index 323c5969..1c805581 100644
--- a/lib/config/testdata/good/everything_blocked.yaml
+++ b/lib/config/testdata/good/everything_blocked.yaml
@@ -1,4 +1,4 @@
bots:
-- name: everything
- user_agent_regex: .*
- action: DENY
+ - name: everything
+ user_agent_regex: .*
+ action: DENY
diff --git a/lib/config/testdata/good/git_client.json b/lib/config/testdata/good/git_client.json
index 68a2b3e8..52c5e11f 100644
--- a/lib/config/testdata/good/git_client.json
+++ b/lib/config/testdata/good/git_client.json
@@ -11,4 +11,4 @@
}
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/git_client.yaml b/lib/config/testdata/good/git_client.yaml
index 44aa2da3..1c496789 100644
--- a/lib/config/testdata/good/git_client.yaml
+++ b/lib/config/testdata/good/git_client.yaml
@@ -1,8 +1,8 @@
bots:
-- name: allow-git-clients
- action: ALLOW
- expression:
- all:
- - userAgent.startsWith("git/") || userAgent.contains("libgit")
- - >
- "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
+ - name: allow-git-clients
+ action: ALLOW
+ expression:
+ all:
+ - userAgent.startsWith("git/") || userAgent.contains("libgit")
+ - >
+ "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
diff --git a/lib/config/testdata/good/import_filesystem.json b/lib/config/testdata/good/import_filesystem.json
index 23480c90..ba0228a7 100644
--- a/lib/config/testdata/good/import_filesystem.json
+++ b/lib/config/testdata/good/import_filesystem.json
@@ -4,4 +4,4 @@
"import": "./testdata/hack-test.json"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/import_filesystem.yaml b/lib/config/testdata/good/import_filesystem.yaml
index 422ccc4e..2ea0d54e 100644
--- a/lib/config/testdata/good/import_filesystem.yaml
+++ b/lib/config/testdata/good/import_filesystem.yaml
@@ -1,2 +1,2 @@
bots:
-- import: ./testdata/hack-test.yaml
\ No newline at end of file
+ - import: ./testdata/hack-test.yaml
diff --git a/lib/config/testdata/good/import_keep_internet_working.json b/lib/config/testdata/good/import_keep_internet_working.json
index 68ff2dbc..06b08fe1 100644
--- a/lib/config/testdata/good/import_keep_internet_working.json
+++ b/lib/config/testdata/good/import_keep_internet_working.json
@@ -4,4 +4,4 @@
"import": "(data)/common/keep-internet-working.yaml"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/import_keep_internet_working.yaml b/lib/config/testdata/good/import_keep_internet_working.yaml
index 923ffe32..19ff33eb 100644
--- a/lib/config/testdata/good/import_keep_internet_working.yaml
+++ b/lib/config/testdata/good/import_keep_internet_working.yaml
@@ -1,2 +1,2 @@
bots:
-- import: (data)/common/keep-internet-working.yaml
\ No newline at end of file
+ - import: (data)/common/keep-internet-working.yaml
diff --git a/lib/config/testdata/good/old_xesite.json b/lib/config/testdata/good/old_xesite.json
index 21816bdd..763d6785 100644
--- a/lib/config/testdata/good/old_xesite.json
+++ b/lib/config/testdata/good/old_xesite.json
@@ -76,4 +76,4 @@
"action": "CHALLENGE"
}
]
-}
\ No newline at end of file
+}
diff --git a/lib/config/testdata/good/status-codes-paranoid.yaml b/lib/config/testdata/good/status-codes-paranoid.yaml
index 89655a3d..0310388b 100644
--- a/lib/config/testdata/good/status-codes-paranoid.yaml
+++ b/lib/config/testdata/good/status-codes-paranoid.yaml
@@ -1,8 +1,8 @@
bots:
-- name: everything
- user_agent_regex: .*
- action: DENY
+ - name: everything
+ user_agent_regex: .*
+ action: DENY
status_codes:
CHALLENGE: 200
- DENY: 200
\ No newline at end of file
+ DENY: 200
diff --git a/lib/config/testdata/good/status-codes-rfc.yaml b/lib/config/testdata/good/status-codes-rfc.yaml
index 4e4e6d8d..c70ea126 100644
--- a/lib/config/testdata/good/status-codes-rfc.yaml
+++ b/lib/config/testdata/good/status-codes-rfc.yaml
@@ -1,8 +1,8 @@
bots:
-- name: everything
- user_agent_regex: .*
- action: DENY
+ - name: everything
+ user_agent_regex: .*
+ action: DENY
status_codes:
CHALLENGE: 403
- DENY: 403
\ No newline at end of file
+ DENY: 403
diff --git a/lib/config/testdata/hack-test.json b/lib/config/testdata/hack-test.json
index 652dcd87..a173bb18 100644
--- a/lib/config/testdata/hack-test.json
+++ b/lib/config/testdata/hack-test.json
@@ -2,8 +2,6 @@
{
"name": "ipv6-ula",
"action": "ALLOW",
- "remote_addresses": [
- "fc00::/7"
- ]
+ "remote_addresses": ["fc00::/7"]
}
-]
\ No newline at end of file
+]
diff --git a/lib/config/testdata/hack-test.yaml b/lib/config/testdata/hack-test.yaml
index cd4d7d05..29263e96 100644
--- a/lib/config/testdata/hack-test.yaml
+++ b/lib/config/testdata/hack-test.yaml
@@ -1,3 +1,3 @@
- name: well-known
path_regex: ^/.well-known/.*$
- action: ALLOW
\ No newline at end of file
+ action: ALLOW
diff --git a/lib/config_test.go b/lib/config_test.go
index 5d392dbb..99aab736 100644
--- a/lib/config_test.go
+++ b/lib/config_test.go
@@ -24,7 +24,6 @@ func TestBadConfigs(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
if _, err := LoadPoliciesOrDefault(t.Context(), filepath.Join("config", "testdata", "bad", st.Name()), anubis.DefaultDifficulty, "info"); err == nil {
t.Fatal(err)
@@ -42,7 +41,6 @@ func TestGoodConfigs(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
t.Run("with-thoth", func(t *testing.T) {
ctx := thothmock.WithMockThoth(t)
diff --git a/lib/http.go b/lib/http.go
index 053470f0..a46cd83d 100644
--- a/lib/http.go
+++ b/lib/http.go
@@ -182,10 +182,7 @@ func makeCode(err error) string {
enc := base64.StdEncoding.EncodeToString(buf.Bytes())
var builder strings.Builder
for i := 0; i < len(enc); i += width {
- end := i + width
- if end > len(enc) {
- end = len(enc)
- }
+ end := min(i+width, len(enc))
builder.WriteString(enc[i:end])
builder.WriteByte('\n')
}
@@ -222,8 +219,12 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C
chall, err := s.issueChallenge(r.Context(), r, lg, cr, rule)
if err != nil {
lg.Error("can't get challenge", "err", err)
+ algorithm := "unknown"
+ if rule.Challenge != nil {
+ algorithm = rule.Challenge.Algorithm
+ }
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
- s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err))
+ s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), algorithm), makeCode(err))
return
}
@@ -248,9 +249,13 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C
impl, ok := challenge.Get(chall.Method)
if !ok {
- lg.Error("check failed", "err", "can't get algorithm", "algorithm", rule.Challenge.Algorithm)
+ algorithm := "unknown"
+ if rule.Challenge != nil {
+ algorithm = rule.Challenge.Algorithm
+ }
+ lg.Error("check failed", "err", "can't get algorithm", "algorithm", algorithm)
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
- s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err))
+ s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), algorithm), makeCode(err))
return
}
@@ -333,7 +338,14 @@ func (s *Server) respondWithError(w http.ResponseWriter, r *http.Request, messag
func (s *Server) respondWithStatus(w http.ResponseWriter, r *http.Request, msg, code string, status int) {
localizer := localization.GetLocalizer(r)
- templ.Handler(web.Base(localizer.T("oh_noes"), web.ErrorPage(msg, s.opts.WebmasterEmail, code, localizer), s.policy.Impressum, localizer), templ.WithStatus(status)).ServeHTTP(w, r)
+ component := web.Base(
+ localizer.T("oh_noes"),
+ web.ErrorPage(msg, s.opts.WebmasterEmail, code, localizer),
+ s.policy.Impressum,
+ localizer,
+ )
+ handler := internal.NoStoreCache(templ.Handler(component, templ.WithStatus(status)))
+ handler.ServeHTTP(w, r)
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
diff --git a/lib/http_test.go b/lib/http_test.go
index 255344f3..bacc8358 100644
--- a/lib/http_test.go
+++ b/lib/http_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/TecharoHQ/anubis"
+ "github.com/TecharoHQ/anubis/internal"
"github.com/TecharoHQ/anubis/lib/policy"
)
@@ -191,3 +192,34 @@ func TestRenderIndexUnauthorized(t *testing.T) {
t.Errorf("expected body %q, got %q", "Authorization required", body)
}
}
+
+func TestNoCacheOnError(t *testing.T) {
+ pol := loadPolicies(t, "testdata/useragent.yaml", 0)
+ srv := spawnAnubis(t, Options{Policy: pol})
+ ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
+ defer ts.Close()
+
+ for userAgent, expectedCacheControl := range map[string]string{
+ "DENY": "no-store",
+ "CHALLENGE": "no-store",
+ "ALLOW": "",
+ } {
+ t.Run(userAgent, func(t *testing.T) {
+ req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ req.Header.Set("User-Agent", userAgent)
+
+ resp, err := ts.Client().Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if resp.Header.Get("Cache-Control") != expectedCacheControl {
+ t.Errorf("wanted Cache-Control header %q, got %q", expectedCacheControl, resp.Header.Get("Cache-Control"))
+ }
+ })
+ }
+}
diff --git a/lib/localization/locales/cs.json b/lib/localization/locales/cs.json
index db4eded3..967da646 100644
--- a/lib/localization/locales/cs.json
+++ b/lib/localization/locales/cs.json
@@ -63,4 +63,4 @@
"js_calculation_error": "Chyba výpočtu!",
"js_calculation_error_msg": "Nepodařilo se vypočítat výzvu:",
"missing_required_forwarded_headers": "Chybějící požadované hlavičky X-Forwarded-*"
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/en.json b/lib/localization/locales/en.json
index 0280b975..4dffb969 100644
--- a/lib/localization/locales/en.json
+++ b/lib/localization/locales/en.json
@@ -63,4 +63,4 @@
"js_finished_reading": "I've finished reading, continue →",
"js_calculation_error": "Calculation error!",
"js_calculation_error_msg": "Failed to calculate challenge:"
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/es.json b/lib/localization/locales/es.json
index 8beb3bf8..d4e91418 100644
--- a/lib/localization/locales/es.json
+++ b/lib/localization/locales/es.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Falló al calcular el desafío:",
"missing_required_forwarded_headers": "Faltan los encabezados X-Forwarded-* requeridos",
"simplified_explanation": "Esta es una medida contra bots y solicitudes maliciosas similar a un CAPTCHA. Sin embargo, en lugar de tener que hacer el trabajo usted mismo, a su navegador se le asigna una tarea de cálculo que debe resolver para garantizar que es un cliente válido. Este concepto se llama
Prueba de trabajo. La tarea se calcula en unos segundos y se le concede acceso al sitio web. Gracias por su comprensión y paciencia."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/et.json b/lib/localization/locales/et.json
index 09125b9c..6ecfd2fa 100644
--- a/lib/localization/locales/et.json
+++ b/lib/localization/locales/et.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Ei suutnud kontrolli arvutada:",
"missing_required_forwarded_headers": "Puuduvad nõutud X-Forwarded-* päised",
"simplified_explanation": "See on meede robotite ja pahatahtlike päringute vastu, mis sarnaneb CAPTCHA-le. Kuid selle asemel, et peaksite ise tööd tegema, antakse teie brauserile arvutusülesanne, mille see peab lahendama, et tagada selle kehtivus kliendina. Seda kontseptsiooni nimetatakse
Töötõendiks. Ülesanne arvutatakse mõne sekundiga ja teile antakse juurdepääs veebisaidile. Täname teid mõistva suhtumise ja kannatlikkuse eest."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/fi.json b/lib/localization/locales/fi.json
index bd06d3a7..fb313c25 100644
--- a/lib/localization/locales/fi.json
+++ b/lib/localization/locales/fi.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Haasteen laskenta ei onnistunut:",
"missing_required_forwarded_headers": "Puuttuvat vaaditut X-Forwarded-* otsikot",
"simplified_explanation": "Tämä on toimenpide botteja ja haitallisia pyyntöjä vastaan, joka on samanlainen kuin CAPTCHA. Sen sijaan, että joutuisit tekemään työtä itse, selaimesi saa laskentatehtävän, joka sen on ratkaistava varmistaakseen, että se on kelvollinen asiakas. Tätä käsitettä kutsutaan nimellä
Työtodistus. Tehtävä lasketaan muutamassa sekunnissa ja saat pääsyn verkkosivustolle. Kiitos ymmärryksestäsi ja kärsivällisyydestäsi."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/fr.json b/lib/localization/locales/fr.json
index b696e04c..13068b7c 100644
--- a/lib/localization/locales/fr.json
+++ b/lib/localization/locales/fr.json
@@ -1,66 +1,66 @@
{
"loading": "Chargement...",
- "why_am_i_seeing": "Pourquoi je vois ceci ?",
+ "why_am_i_seeing": "Comment suis-je arrivé·e ici ?",
"protected_by": "Protégé par",
- "protected_from": "From",
+ "protected_from": "de",
"made_with": "Fait avec ❤️ au 🇨🇦",
"mascot_design": "Design de la mascotte par",
- "ai_companies_explanation": "Vous voyez ceci car l'administrateur de ce site web a configuré Anubis pour protéger le serveur contre le fléau des entreprises d'IA qui scrapent agressivement les sites web. Cela peut et cause des temps d'arrêt pour les sites web, ce qui rend leurs ressources inaccessibles pour tout le monde.",
- "anubis_compromise": "Anubis est un compromis. Anubis utilise un schéma de Preuve de Travail dans la veine de Hashcash, un schéma de preuve de travail proposé pour réduire le spam par email. L'idée est qu'à l'échelle individuelle, la charge supplémentaire est négligeable, mais à l'échelle des scrapers de masse, cela s'accumule et rend le scraping beaucoup plus coûteux.",
- "hack_purpose": "En fin de compte, il s'agit d'une solution de substitution afin de consacrer plus de temps à l'identification et à l'empreinte digitale des navigateurs sans tête (par exemple, via leur rendu de police) afin que la page de preuve de travail du défi n'ait pas besoin d'être présentée aux utilisateurs qui sont beaucoup plus susceptibles d'être légitimes.",
- "jshelter_note": "Veuillez noter qu'Anubis nécessite l'utilisation de fonctionnalités JavaScript modernes que des plugins comme JShelter désactiveront. Veuillez désactiver JShelter ou d'autres plugins similaires pour ce domaine.",
- "version_info": "Ce site web utilise Anubis version",
+ "ai_companies_explanation": "Vous voyez cette page car l'administrateur·rice de ce site Web a configuré Anubis pour protéger le serveur contre le fléau des entreprises d'IA qui récupèrent agressivement le contenu des sites Web. Cela perturbe leur fonctionnement et rend leurs ressources inaccessibles pour tout le monde.",
+ "anubis_compromise": "Anubis est un compromis. Anubis utilise un procédé de preuve de travail similaire à Hashcash, un procédé de preuve de travail proposé pour réduire le spam par e-mail. L'idée est qu'à l'échelle individuelle, la charge supplémentaire est négligeable, mais à l'échelle des scrapers de masse, la charge s'accumule et le scraping devient beaucoup plus coûteux.",
+ "hack_purpose": "En fin de compte, il s'agit d'une solution de substitution permettant de consacrer plus de temps à l'identification et à la prise d'empreintes des navigateurs headless (par exemple, en reconnaissant leur rendu des polices), pour que, à terme, la page de défi utilisant la preuve de travail n'ait plus besoin d'être présentée aux utilisateur·rices qui sont beaucoup plus susceptibles d'être légitimes.",
+ "jshelter_note": "Veuillez noter qu'Anubis nécessite l'utilisation de fonctionnalités JavaScript modernes qui peuvent être désactivées par des plugins comme JShelter. Veuillez désactiver JShelter ou tout autre plugin similaire pour ce domaine.",
+ "version_info": "Ce site Web utilise Anubis version",
"try_again": "Réessayer",
"go_home": "Accueil",
- "contact_webmaster": "ou si vous pensez que vous ne devriez pas être bloqué, veuillez contacter le webmaster à",
+ "contact_webmaster": "ou si vous pensez que vous ne devriez pas être bloqué, veuillez contacter le webmaster à l'adresse",
"connection_security": "Veuillez patienter un instant pendant que nous assurons la sécurité de votre connexion.",
- "javascript_required": "Malheureusement, vous devez activer JavaScript pour passer ce défi. Ceci est requis car les entreprises d'IA ont changé le contrat social autour du fonctionnement de l'hébergement de sites web. Une solution sans JS est en cours de développement.",
+ "javascript_required": "Malheureusement, vous devez activer JavaScript pour passer cette page de défi. Cette obligation est imposée par les entreprises d'IA, qui ont décidé de modifier unilatéralement les termes du contrat social régissant l'hébergement de sites Web. Une solution sans JavaScript est en cours de développement.",
"benchmark_requires_js": "L'exécution de l'outil de benchmark nécessite l'activation de JavaScript.",
- "difficulty": "Difficulté :",
- "algorithm": "Algorithme :",
- "compare": "Comparer :",
+ "difficulty": "Difficulté :",
+ "algorithm": "Algorithme :",
+ "compare": "Comparer :",
"time": "Temps",
"iters": "Itérations",
"time_a": "Temps A",
"iters_a": "Itér. A",
"time_b": "Temps B",
"iters_b": "Itér. B",
- "static_check_endpoint": "Ceci est juste un point de terminaison de vérification pour votre proxy inverse à utiliser.",
+ "static_check_endpoint": "Ceci est juste un point de terminaison de vérification à utiliser par votre proxy inverse.",
"authorization_required": "Autorisation requise",
- "cookies_disabled": "Votre navigateur est configuré pour désactiver les cookies. Anubis nécessite des cookies pour l'intérêt légitime de s'assurer que vous êtes un client valide. Veuillez activer les cookies pour ce domaine",
- "access_denied": "Accès refusé : code d'erreur",
- "dronebl_entry": "DroneBL a signalé une entrée",
+ "cookies_disabled": "Les cookies sont désactivés dans votre navigateur. Anubis a recours aux cookies pour l'intérêt légitime de s'assurer que vous êtes un client valide. Veuillez activer les cookies pour ce domaine.",
+ "access_denied": "Accès refusé : code d'erreur",
+ "dronebl_entry": "DroneBL a rapporté une entrée",
"see_dronebl_lookup": "voir",
- "internal_server_error": "Erreur interne du serveur : l'administrateur a mal configuré Anubis. Veuillez contacter l'administrateur et lui demander de consulter les logs autour de",
+ "internal_server_error": "Erreur interne du serveur : l'administrateur·rice a mal configuré Anubis. Veuillez contacter l'administrateur·rice et lui demander de consulter les logs autour de",
"invalid_redirect": "Redirection invalide",
"redirect_not_parseable": "URL de redirection non analysable",
"redirect_domain_not_allowed": "Domaine de redirection non autorisé",
- "failed_to_sign_jwt": "échec de la signature JWT",
+ "failed_to_sign_jwt": "échec de la signature du JWT",
"invalid_invocation": "Invocation invalide de MakeChallenge",
- "client_error_browser": "Erreur client : Veuillez vous assurer que votre navigateur est à jour et réessayez plus tard.",
- "oh_noes": "Oh non !",
- "benchmarking_anubis": "Test de performance d'Anubis !",
- "you_are_not_a_bot": "Vous n'êtes pas un robot !",
- "making_sure_not_bot": "Vérification que vous n'êtes pas un robot !",
- "celphase": "PHASE de CEL",
- "js_web_crypto_error": "Votre navigateur n'a pas d'élément web.crypto fonctionnel. Consultez-vous cette page dans un contexte sécurisé ?",
- "js_web_workers_error": "Votre navigateur ne prend pas en charge les web workers (Anubis les utilise pour éviter de bloquer votre navigateur). Avez-vous un plugin comme JShelter installé ?",
- "js_cookies_error": "Votre navigateur ne stocke pas les cookies. Anubis utilise des cookies pour déterminer quels clients ont réussi les défis en stockant un jeton signé dans un cookie. Veuillez activer le stockage des cookies pour ce domaine. Les noms des cookies qu'Anubis stocke peuvent varier sans préavis. Les noms et valeurs des cookies ne font pas partie de l'API publique.",
- "js_context_not_secure": "Votre contexte n'est pas sécurisé !",
- "js_context_not_secure_msg": "Essayez de vous connecter via HTTPS ou informez l'administrateur de configurer HTTPS. Pour plus d'informations, voir
MDN.",
+ "client_error_browser": "Erreur client : Veuillez vous assurer que votre navigateur est à jour et réessayez plus tard.",
+ "oh_noes": "Oh non !",
+ "benchmarking_anubis": "Je vérifie les performances d'Anubis !",
+ "you_are_not_a_bot": "Vous n'êtes pas un robot !",
+ "making_sure_not_bot": "Je m'assure que vous n'êtes pas un robot !",
+ "celphase": "CELPHASE",
+ "js_web_crypto_error": "L'élément web.crypto de votre navigateur n'est pas fonctionnel. Consultez-vous bien cette page dans un contexte sécurisé ?",
+ "js_web_workers_error": "Votre navigateur ne prend pas en charge les web workers (Anubis les utilise pour éviter de bloquer votre navigateur). Avez-vous installé un plugin comme JShelter ?",
+ "js_cookies_error": "Votre navigateur ne stocke pas les cookies. Anubis a recours aux cookies pour déterminer quels clients ont réussi les défis en stockant un jeton signé dans un cookie. Veuillez activer le stockage des cookies pour ce domaine. Le nom des cookies stockés par Anubis peut varier à tout moment. Le nom et la valeur des cookies ne font pas partie de l'API publique.",
+ "js_context_not_secure": "Votre contexte n'est pas sécurisé !",
+ "js_context_not_secure_msg": "Essayez de vous connecter via HTTPS ou demandez à l'administrateur·rice de configurer HTTPS. Pour plus d'informations, consultez
MDN.",
"js_calculating": "Calcul en cours...",
"js_missing_feature": "Fonctionnalité manquante",
- "js_challenge_error": "Erreur de défi !",
+ "js_challenge_error": "Erreur de défi !",
"js_challenge_error_msg": "Échec de la résolution de l'algorithme de vérification. Vous pouvez essayer de recharger la page.",
- "js_calculating_difficulty": "Calcul en cours...
Difficulté :",
- "js_speed": "Vitesse :",
+ "js_calculating_difficulty": "Calcul en cours...
Difficulté :",
+ "js_speed": "Vitesse :",
"js_verification_longer": "La vérification prend plus de temps que prévu. Veuillez ne pas actualiser la page.",
- "js_success": "Succès !",
- "js_done_took": "Terminé ! A pris",
+ "js_success": "Vérification réussie !",
+ "js_done_took": "Terminé ! Cela aura nécessité",
"js_iterations": "itérations",
- "js_finished_reading": "J'ai fini de lire, continuer →",
- "js_calculation_error": "Erreur de calcul !",
- "js_calculation_error_msg": "Échec du calcul du défi :",
- "missing_required_forwarded_headers": "En-têtes X-Forwarded-* requis manquants",
- "simplified_explanation": "Il s'agit d'une mesure contre les robots et les requêtes malveillantes similaire à un CAPTCHA. Cependant, au lieu d'avoir à faire le travail vous-même, votre navigateur se voit confier une tâche de calcul qu'il doit résoudre pour s'assurer qu'il est un client valide. Ce concept s'appelle
Preuve de travail. La tâche est calculée en quelques secondes et vous avez accès au site Web. Merci de votre compréhension et de votre patience."
-}
\ No newline at end of file
+ "js_finished_reading": "J'ai fini de lire, continuer →",
+ "js_calculation_error": "Erreur de calcul !",
+ "js_calculation_error_msg": "Échec du calcul du défi :",
+ "missing_required_forwarded_headers": "En-têtes X-Forwarded-* manquants",
+ "simplified_explanation": "Ceci est une mesure contre les robots et les requêtes malveillantes, similaire à un CAPTCHA. Cependant, au lieu d'avoir à faire le travail vous-même, votre navigateur se voit confier une tâche de calcul qu'il doit résoudre pour confirmer qu'il est un client valide. Ce concept est nommé
Preuve de travail. La tâche s'effectue en quelques secondes, puis vous avez accès au site Web. Merci pour votre compréhension et votre patience."
+}
diff --git a/lib/localization/locales/it.json b/lib/localization/locales/it.json
index e2ecc11b..4b44c523 100644
--- a/lib/localization/locales/it.json
+++ b/lib/localization/locales/it.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Impossibile superare il test:",
"missing_required_forwarded_headers": "Mancano gli header X-Forwarded-* richiesti",
"simplified_explanation": "Questa è una misura contro bot e richieste dannose simile a un CAPTCHA. Tuttavia, invece di dover lavorare tu stesso, al tuo browser viene assegnato un compito di calcolo che deve risolvere per garantire che sia un client valido. Questo concetto è chiamato
Proof of Work. Il compito viene calcolato in pochi secondi e ti viene concesso l'accesso al sito web. Grazie per la tua comprensione e pazienza."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/ja.json b/lib/localization/locales/ja.json
index 3042173d..791c963f 100644
--- a/lib/localization/locales/ja.json
+++ b/lib/localization/locales/ja.json
@@ -9,7 +9,7 @@
"anubis_compromise": "Anubisは妥協策です。AnubisはHashcashのようなProof-of-Work方式を採用しており、これは元々メールスパムを減らすために提案された仕組みです。個人レベルでは追加の負荷は無視できる程度ですが、大規模なスクレイピングでは負荷が積み重なり、スクレイピングのコストが大幅に増加します。",
"hack_purpose": "最終的に、これはヘッドレスブラウザのフィンガープリントと識別に時間を費やすためのプレースホルダーソリューションです(例:フォントレンダリングの方法による)。これにより、正当なユーザーにはチャレンジのプルーフオブワークページを提示する必要がなくなります。",
"jshelter_note": "Anubisは、JShelterのようなプラグインが無効化する最新のJavaScript機能を必要とします。このドメインではJShelterや同様のプラグインを無効にしてください。",
- "version_info": "このウェブサイトはAnubisバージョンで動作しています",
+ "version_info": "このウェブサイトはAnubisで動作しています バージョン",
"try_again": "再試行",
"go_home": "ホームに戻る",
"contact_webmaster": "もしブロックされるべきでないと思われる場合は、ウェブマスターにご連絡ください:",
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "チャレンジの計算に失敗しました:",
"missing_required_forwarded_headers": "必要な X-Forwarded-* ヘッダーがありません",
"simplified_explanation": "これは、CAPTCHAと同様の、ボットや悪意のあるリクエストに対する対策です。ただし、自分で作業する代わりに、ブラウザに計算タスクが与えられ、それを解決して有効なクライアントであることを確認する必要があります。この概念は
Proof of Workと呼ばれます。タスクは数秒で計算され、ウェブサイトへのアクセスが許可されます。ご理解とご協力をお願いいたします。"
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/nb.json b/lib/localization/locales/nb.json
index 0b8b6fcc..ada36f20 100644
--- a/lib/localization/locales/nb.json
+++ b/lib/localization/locales/nb.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Mislyktes i å beregne utfordring:",
"missing_required_forwarded_headers": "Mangler nødvendige X-Forwarded-* header",
"simplified_explanation": "Dette er et tiltak mot roboter og ondsinnede forespørsler som ligner på en CAPTCHA. Men i stedet for å måtte gjøre arbeidet selv, får nettleseren din en beregningsoppgave som den må løse for å sikre at den er en gyldig klient. Dette konseptet kalles
Proof of Work. Oppgaven beregnes på noen få sekunder, og du får tilgang til nettstedet. Takk for din forståelse og tålmodighet."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/nl.json b/lib/localization/locales/nl.json
index 20710847..bce5a2cf 100644
--- a/lib/localization/locales/nl.json
+++ b/lib/localization/locales/nl.json
@@ -4,18 +4,18 @@
"protected_by": "Beschermd door",
"protected_from": "Van",
"made_with": "Gemaakt met ❤️ in 🇨🇦",
- "mascot_design": "Mascotte ontwerp door",
- "ai_companies_explanation": "Je ziet dit omdat de beheerder van deze website Anubis heeft ingesteld om de server te beschermen tegen de plaag van AI-bedrijven die agressief websites scrapen. Dit kan downtime veroorzaken voor de websites, waardoor hun bronnen voor iedereen ontoegankelijk worden.",
- "anubis_compromise": "Anubis is een compromis. Anubis gebruikt een Proef-van-Taak schema in de geest van Hashcash, een voorgesteld proef-van-taak schema voor het verminderen van e-mailspam. Het idee is dat op individuele schalen de extra belasting niet waarneembaar is, maar op het niveau van massascrapers telt het op en maakt het scrapen veel duurder.",
+ "mascot_design": "Mascotte-ontwerp door",
+ "ai_companies_explanation": "Je ziet dit omdat de beheerder van deze website Anubis heeft ingesteld om de server te beschermen tegen de plaag van AI-bedrijven die agressief websites scrapen. Dit kan downtime veroorzaken voor de websites, waardoor de website voor iedereen ontoegankelijk wordt.",
+ "anubis_compromise": "Anubis is een compromis. Anubis gebruikt een proof-of-work-algoritme in de geest van Hashcash, een proof-of-work-algoritme voor het verminderen van e-mailspam. Het idee is dat voor individuen minimaal is, maar het voor scrapers veel duurder wordt.",
"hack_purpose": "Uiteindelijk is dit een tijdelijke oplossing, zodat er meer tijd kan worden besteed aan het identificeren en herkennen van headless browsers (bijv. via de manier waarop ze lettertypen renderen), zodat de proof-of-work-pagina niet hoeft te worden gepresenteerd aan gebruikers die veel waarschijnlijker legitiem zijn.",
- "jshelter_note": "Anubis vereist het gebruik van moderne JavaScript-functies die worden uitgeschakeld door plugins zoals JShelter. Schakel JShelter of andere dergelijke plugins uit voor dit domein.",
+ "jshelter_note": "Anubis vereist het gebruik van moderne JavaScript-functies die worden uitgeschakeld door plugins zoals JShelter. Schakel JShelter of soortgelijke plugins uit voor dit domein.",
"version_info": "Deze website draait op de Anubis-versie",
"try_again": "Probeer opnieuw",
- "go_home": "Naar de thuispagina",
- "contact_webmaster": "of als u denkt dat u niet geblokkeerd zou moeten worden, neem dan contact op met de webmaster op",
- "connection_security": "Wacht even terwijl we de veiligheid van uw verbinding waarborgen.",
+ "go_home": "Naar de hoofdpagine",
+ "contact_webmaster": "of als je denkt dat je niet geblokkeerd had moeten worden, neem contact op met de webmaster op",
+ "connection_security": "Wacht even terwijl we de veiligheid van je verbinding waarborgen.",
"javascript_required": "Helaas moet je JavaScript inschakelen om voorbij deze uitdaging te komen. Dit is nodig omdat AI-bedrijven het sociale contract rond de werking van websitehosting hebben veranderd. Een oplossing zonder JavaScript is nog in ontwikkeling.",
- "benchmark_requires_js": "Voor het uitvoeren van de vergelijkingsinstrument moet JavaScript zijn ingeschakeld.",
+ "benchmark_requires_js": "Voor het uitvoeren van de check moet JavaScript zijn ingeschakeld.",
"difficulty": "Moeilijkheidsgraad:",
"algorithm": "Algoritme:",
"compare": "Vergelijken:",
@@ -25,36 +25,36 @@
"iters_a": "Iters A",
"time_b": "Tijd B",
"iters_b": "Iters B",
- "static_check_endpoint": "Dit is gewoon een controle-eindpunt voor uw reverse proxy om te gebruiken.",
+ "static_check_endpoint": "Dit is gewoon een controle-eindpunt voor je reverse proxy om te gebruiken.",
"authorization_required": "Autorisatie vereist",
- "cookies_disabled": "Uw browser is geconfigureerd om cookies uit te schakelen. Anubis heeft cookies nodig om er zeker van te zijn dat u een geldige klant bent. Schakel cookies in voor dit domein",
+ "cookies_disabled": "Cookies zijn uitgeschakeld in je browser. Anubis heeft cookies nodig om er zeker van te zijn dat je een echt persoon bent. Schakel cookies in voor dit domein",
"access_denied": "Toegang geweigerd: foutcode",
"dronebl_entry": "DroneBL meldde een item",
"see_dronebl_lookup": "zie",
- "internal_server_error": "Interne Serverfout: beheerder heeft Anubis verkeerd geconfigureerd. Neem contact op met de beheerder en vraag of hij/zij de logs rond kan kijken",
+ "internal_server_error": "Interne Serverfout: beheerder heeft Anubis verkeerd geconfigureerd. Vraag de beheerder om de logs te bekijken.",
"invalid_redirect": "Ongeldige omleiding",
- "redirect_not_parseable": "Redirect URL niet parseerbaar",
+ "redirect_not_parseable": "Redirect-URL kan niet verwerkt worden",
"redirect_domain_not_allowed": "Redirect-domein niet toegestaan",
- "failed_to_sign_jwt": "jWT niet ondertekend",
+ "failed_to_sign_jwt": "JWT niet ondertekend",
"invalid_invocation": "Ongeldige aanroep van MakeChallenge",
- "client_error_browser": "Fout bij klant: Controleer of uw browser bijgewerkt is en probeer het later opnieuw.",
+ "client_error_browser": "Fout bij client: Controleer of je browser bijgewerkt is en probeer het later opnieuw.",
"oh_noes": "Oh nee-tjes!",
"benchmarking_anubis": "Anubis benchmarken!",
"you_are_not_a_bot": "Je bent geen bot!",
- "making_sure_not_bot": "Ervoor zorgen dat je geen bot bent!",
+ "making_sure_not_bot": "Even checken of je een bot bent!",
"celphase": "CELPHASE",
- "js_web_crypto_error": "Uw browser heeft geen werkend web.crypto-element. Bekijkt u dit via een beveiligde context?",
+ "js_web_crypto_error": "Je browser heeft geen werkend web.crypto-element. Bekijkt u dit via een beveiligde context?",
"js_web_workers_error": "Je browser ondersteunt geen web-takers (Anubis gebruikt dit om te voorkomen dat je browser bevriest). Heb je een plugin zoals JShelter geïnstalleerd?",
- "js_cookies_error": "Uw browser slaat geen cookies op. Anubis gebruikt cookies om te bepalen welke klanten geslaagd zijn voor uitdagingen door een ondertekend token in een cookie op te slaan. Schakel het opslaan van cookies voor dit domein in. De namen van de cookies die Anubis opslaat, kunnen zonder voorafgaande kennisgeving variëren. cookiesnamen en -waarden maken geen deel uit van de openbare API.",
+ "js_cookies_error": "Je browser slaat geen cookies op. Anubis gebruikt cookies om te bepalen welke bezoekers echte personen zijn. Schakel het opslaan van cookies voor dit domein in. De namen van de cookies die Anubis opslaat, kunnen in de toekomst veranderen. De namen en waarden van cookies maken geen deel uit van de openbare API.",
"js_context_not_secure": "Je context is niet veilig!",
"js_context_not_secure_msg": "Probeer verbinding te maken via HTTPS of laat de beheerder weten dat HTTPS moet worden ingesteld. Zie
MDN voor meer informatie.",
"js_calculating": "Berekenen...",
"js_missing_feature": "Ontbrekende functie",
"js_challenge_error": "Uitdagingsfout!",
- "js_challenge_error_msg": "Mislukt bij het oplossen van het controlealgoritme. Misschien wilt u de pagina opnieuw laden.",
+ "js_challenge_error_msg": "De check is gefaald. Misschien wil je de pagina opnieuw laden.",
"js_calculating_difficulty": "Rekenen...
Moeilijkheidsgraad:",
"js_speed": "Snelheid:",
- "js_verification_longer": "Verificatie duurt langer dan verwacht. Vernieuw de pagina niet a.u.b.",
+ "js_verification_longer": "Verificatie duurt langer dan verwacht. Ververs de pagina niet.",
"js_success": "Gelukt!",
"js_done_took": "Klaar! Nam",
"js_iterations": "iteraties",
@@ -62,5 +62,5 @@
"js_calculation_error": "Rekenfout!",
"js_calculation_error_msg": "Uitdaging niet berekend:",
"missing_required_forwarded_headers": "Ontbrekende vereiste X-Forwarded-* headers",
- "simplified_explanation": "Dit is een maatregel tegen bots en kwaadwillende verzoeken, vergelijkbaar met een CAPTCHA. In plaats van dat u zelf werk moet verrichten, krijgt uw browser een rekentaak die hij moet oplossen om ervoor te zorgen dat het een geldige client is. Dit concept wordt
Proof of Work genoemd. De taak wordt in een paar seconden berekend en u krijgt toegang tot de website. Bedankt voor uw begrip en geduld."
+ "simplified_explanation": "Dit is een maatregel tegen bots en kwaadwillende verzoeken, vergelijkbaar met een CAPTCHA. In plaats van dat je zelf werk moet verrichten, krijgt je browser een rekentaak die moet worden opgelost om ervoor te zorgen dat het een geldige client is. Dit concept wordt
Proof of Work genoemd. De taak wordt in een paar seconden berekend en u krijgt toegang tot de website. Bedankt voor je begrip en geduld."
}
diff --git a/lib/localization/locales/pl.json b/lib/localization/locales/pl.json
index 47e84c31..b7e7f6e8 100644
--- a/lib/localization/locales/pl.json
+++ b/lib/localization/locales/pl.json
@@ -1,66 +1,66 @@
{
- "loading": "Ładowanie...",
- "why_am_i_seeing": "Dlaczego to widzę?",
- "protected_by": "Chronione przez",
- "protected_from": "Przed",
- "made_with": "Stworzone z ❤️ w 🇨🇦",
- "mascot_design": "Projekt maskotki:",
- "ai_companies_explanation": "Widzisz to, ponieważ administrator tej strony skonfigurował Anubisa, aby chronić serwer przed masowym skanowaniem treści przez firmy tworzące AI. Powoduje to obciążenie i przestoje, przez co zasoby strony stają się niedostępne dla wszystkich.",
- "anubis_compromise": "Anubis jest kompromisem. Używa mechanizmu Proof-of-Work w stylu Hashcash — proponowanego systemu ograniczania spamu e-mail. Pomysł polega na tym, że dla indywidualnych użytkowników dodatkowe obciążenie jest niezauważalne, ale w skali masowego skanowania koszt szybko rośnie.",
- "hack_purpose": "Docelowo jest to rozwiązanie tymczasowe, aby zyskać czas na ulepszenie metod identyfikacji przeglądarek bez interfejsu graficznego (np. poprzez analizę renderowania czcionek), by w przyszłości nie musieć wyświetlać strony z zadaniem Proof-of-Work użytkownikom, którzy najprawdopodobniej są prawidłowi.",
- "simplified_explanation": "To zabezpieczenie przed botami i złośliwymi żądaniami, podobne do CAPTCHA. Jednak zamiast wykonywać zadanie samodzielnie, przeglądarka otrzymuje obliczenie do wykonania, aby potwierdzić, że jest prawidłowym klientem. Ten mechanizm to
Proof of Work. Zadanie trwa kilka sekund i uzyskujesz dostęp do strony. Dziękujemy za cierpliwość.",
- "jshelter_note": "Uwaga: Anubis wymaga nowoczesnych funkcji JavaScript, które wtyczki typu JShelter mogą blokować. Wyłącz JShelter lub podobne dodatki dla tej domeny.",
- "version_info": "Ta strona działa na Anubis w wersji",
- "try_again": "Spróbuj ponownie",
- "go_home": "Wróć na stronę główną",
- "contact_webmaster": "lub jeśli uważasz, że nie powinieneś być blokowany, skontaktuj się z administratorem pod adresem",
- "connection_security": "Poczekaj chwilę, sprawdzamy bezpieczeństwo Twojego połączenia.",
- "javascript_required": "Niestety, aby przejść tę próbę, musisz włączyć obsługę JavaScript. Jest to konieczne, ponieważ firmy zajmujące się sztuczną inteligencją zmieniły umowę społeczną dotyczącą funkcjonowania hostingu stron internetowych. Rozwiązanie bez obsługi JavaScript jest w trakcie opracowywania.",
- "benchmark_requires_js": "Uruchomienie narzędzia testowego wymaga włączonego JavaScript.",
- "difficulty": "Trudność:",
- "algorithm": "Algorytm:",
- "compare": "Porównaj:",
- "time": "Czas",
- "iters": "Iteracje",
- "time_a": "Czas A",
- "iters_a": "Iteracje A",
- "time_b": "Czas B",
- "iters_b": "Iteracje B",
- "static_check_endpoint": "To jedynie punkt kontrolny do użytku przez Twój reverse proxy.",
- "authorization_required": "Wymagane uwierzytelnienie",
- "cookies_disabled": "Twoja przeglądarka blokuje ciasteczka. Anubis wymaga ich, aby potwierdzić, że jesteś prawidłowym klientem. Włącz ciasteczka dla tej domeny.",
- "access_denied": "Brak dostępu: kod błędu",
- "dronebl_entry": "DroneBL zgłosił wpis",
- "see_dronebl_lookup": "zobacz",
- "internal_server_error": "Błąd wewnętrzny serwera: administrator błędnie skonfigurował Anubis. Skontaktuj się z administratorem i poproś o sprawdzenie logów",
- "invalid_redirect": "Nieprawidłowe przekierowanie",
- "redirect_not_parseable": "Nie można odczytać adresu przekierowania",
- "redirect_domain_not_allowed": "Domena przekierowania niedozwolona",
- "missing_required_forwarded_headers": "Brak wymaganych nagłówków X-Forwarded-*",
- "failed_to_sign_jwt": "Nie udało się podpisać JWT",
- "invalid_invocation": "Nieprawidłowe wywołanie MakeChallenge",
- "client_error_browser": "Błąd klienta: upewnij się, że Twoja przeglądarka jest aktualna i spróbuj ponownie później.",
- "oh_noes": "O nie!",
- "benchmarking_anubis": "Testowanie wydajności Anubis!",
- "you_are_not_a_bot": "Nie jesteś botem!",
- "making_sure_not_bot": "Sprawdzamy, czy nie jesteś botem!",
- "celphase": "CELPHASE",
- "js_web_crypto_error": "Twoja przeglądarka nie obsługuje web.crypto. Czy korzystasz z bezpiecznego połączenia?",
- "js_web_workers_error": "Twoja przeglądarka nie obsługuje web workers (Anubis ich używa, by nie zawieszać przeglądarki). Czy masz zainstalowaną wtyczkę typu JShelter?",
- "js_cookies_error": "Twoja przeglądarka nie zapisuje ciasteczek. Anubis używa ich do przechowywania podpisanego tokenu potwierdzającego przejście zabezpieczenia. Włącz zapis ciasteczek dla tej domeny. Nazwy ciasteczek mogą zmieniać się bez zapowiedzi. Nazwy oraz zawartość ciasteczek nie są cześcią publicznego API.",
- "js_context_not_secure": "Kontekst nie jest bezpieczny!",
- "js_context_not_secure_msg": "Spróbuj połączyć się przez HTTPS lub poinformuj administratora, by skonfigurował HTTPS. Więcej informacji na
MDN.",
- "js_calculating": "Obliczanie...",
- "js_missing_feature": "Brakująca funkcja",
- "js_challenge_error": "Błąd wyzwania!",
- "js_challenge_error_msg": "Nie udało się ustalić algorytmu sprawdzającego. Możesz spróbować odświeżyć stronę.",
- "js_calculating_difficulty": "Obliczanie...
Trudność:",
- "js_speed": "Prędkość:",
- "js_verification_longer": "Weryfikacja trwa dłużej niż zwykle. Proszę nie odświeżać strony.",
- "js_success": "Sukces!",
- "js_done_took": "Gotowe! Zajęło to",
- "js_iterations": "iteracji",
- "js_finished_reading": "Skończyłem czytać, kontynuuj →",
- "js_calculation_error": "Błąd obliczeń!",
- "js_calculation_error_msg": "Nie udało się obliczyć zadania:"
-}
\ No newline at end of file
+ "loading": "Ładowanie...",
+ "why_am_i_seeing": "Dlaczego to widzę?",
+ "protected_by": "Chronione przez",
+ "protected_from": "Przed",
+ "made_with": "Stworzone z ❤️ w 🇨🇦",
+ "mascot_design": "Projekt maskotki:",
+ "ai_companies_explanation": "Widzisz to, ponieważ administrator tej strony skonfigurował Anubisa, aby chronić serwer przed masowym skanowaniem treści przez firmy tworzące AI. Powoduje to obciążenie i przestoje, przez co zasoby strony stają się niedostępne dla wszystkich.",
+ "anubis_compromise": "Anubis jest kompromisem. Używa mechanizmu Proof-of-Work w stylu Hashcash — proponowanego systemu ograniczania spamu e-mail. Pomysł polega na tym, że dla indywidualnych użytkowników dodatkowe obciążenie jest niezauważalne, ale w skali masowego skanowania koszt szybko rośnie.",
+ "hack_purpose": "Docelowo jest to rozwiązanie tymczasowe, aby zyskać czas na ulepszenie metod identyfikacji przeglądarek bez interfejsu graficznego (np. poprzez analizę renderowania czcionek), by w przyszłości nie musieć wyświetlać strony z zadaniem Proof-of-Work użytkownikom, którzy najprawdopodobniej są prawidłowi.",
+ "simplified_explanation": "To zabezpieczenie przed botami i złośliwymi żądaniami, podobne do CAPTCHA. Jednak zamiast wykonywać zadanie samodzielnie, przeglądarka otrzymuje obliczenie do wykonania, aby potwierdzić, że jest prawidłowym klientem. Ten mechanizm to
Proof of Work. Zadanie trwa kilka sekund i uzyskujesz dostęp do strony. Dziękujemy za cierpliwość.",
+ "jshelter_note": "Uwaga: Anubis wymaga nowoczesnych funkcji JavaScript, które wtyczki typu JShelter mogą blokować. Wyłącz JShelter lub podobne dodatki dla tej domeny.",
+ "version_info": "Ta strona działa na Anubis w wersji",
+ "try_again": "Spróbuj ponownie",
+ "go_home": "Wróć na stronę główną",
+ "contact_webmaster": "lub jeśli uważasz, że nie powinieneś być blokowany, skontaktuj się z administratorem pod adresem",
+ "connection_security": "Poczekaj chwilę, sprawdzamy bezpieczeństwo Twojego połączenia.",
+ "javascript_required": "Niestety, aby przejść tę próbę, musisz włączyć obsługę JavaScript. Jest to konieczne, ponieważ firmy zajmujące się sztuczną inteligencją zmieniły umowę społeczną dotyczącą funkcjonowania hostingu stron internetowych. Rozwiązanie bez obsługi JavaScript jest w trakcie opracowywania.",
+ "benchmark_requires_js": "Uruchomienie narzędzia testowego wymaga włączonego JavaScript.",
+ "difficulty": "Trudność:",
+ "algorithm": "Algorytm:",
+ "compare": "Porównaj:",
+ "time": "Czas",
+ "iters": "Iteracje",
+ "time_a": "Czas A",
+ "iters_a": "Iteracje A",
+ "time_b": "Czas B",
+ "iters_b": "Iteracje B",
+ "static_check_endpoint": "To jedynie punkt kontrolny do użytku przez Twój reverse proxy.",
+ "authorization_required": "Wymagane uwierzytelnienie",
+ "cookies_disabled": "Twoja przeglądarka blokuje ciasteczka. Anubis wymaga ich, aby potwierdzić, że jesteś prawidłowym klientem. Włącz ciasteczka dla tej domeny.",
+ "access_denied": "Brak dostępu: kod błędu",
+ "dronebl_entry": "DroneBL zgłosił wpis",
+ "see_dronebl_lookup": "zobacz",
+ "internal_server_error": "Błąd wewnętrzny serwera: administrator błędnie skonfigurował Anubis. Skontaktuj się z administratorem i poproś o sprawdzenie logów",
+ "invalid_redirect": "Nieprawidłowe przekierowanie",
+ "redirect_not_parseable": "Nie można odczytać adresu przekierowania",
+ "redirect_domain_not_allowed": "Domena przekierowania niedozwolona",
+ "missing_required_forwarded_headers": "Brak wymaganych nagłówków X-Forwarded-*",
+ "failed_to_sign_jwt": "Nie udało się podpisać JWT",
+ "invalid_invocation": "Nieprawidłowe wywołanie MakeChallenge",
+ "client_error_browser": "Błąd klienta: upewnij się, że Twoja przeglądarka jest aktualna i spróbuj ponownie później.",
+ "oh_noes": "O nie!",
+ "benchmarking_anubis": "Testowanie wydajności Anubis!",
+ "you_are_not_a_bot": "Nie jesteś botem!",
+ "making_sure_not_bot": "Sprawdzamy, czy nie jesteś botem!",
+ "celphase": "CELPHASE",
+ "js_web_crypto_error": "Twoja przeglądarka nie obsługuje web.crypto. Czy korzystasz z bezpiecznego połączenia?",
+ "js_web_workers_error": "Twoja przeglądarka nie obsługuje web workers (Anubis ich używa, by nie zawieszać przeglądarki). Czy masz zainstalowaną wtyczkę typu JShelter?",
+ "js_cookies_error": "Twoja przeglądarka nie zapisuje ciasteczek. Anubis używa ich do przechowywania podpisanego tokenu potwierdzającego przejście zabezpieczenia. Włącz zapis ciasteczek dla tej domeny. Nazwy ciasteczek mogą zmieniać się bez zapowiedzi. Nazwy oraz zawartość ciasteczek nie są cześcią publicznego API.",
+ "js_context_not_secure": "Kontekst nie jest bezpieczny!",
+ "js_context_not_secure_msg": "Spróbuj połączyć się przez HTTPS lub poinformuj administratora, by skonfigurował HTTPS. Więcej informacji na
MDN.",
+ "js_calculating": "Obliczanie...",
+ "js_missing_feature": "Brakująca funkcja",
+ "js_challenge_error": "Błąd wyzwania!",
+ "js_challenge_error_msg": "Nie udało się ustalić algorytmu sprawdzającego. Możesz spróbować odświeżyć stronę.",
+ "js_calculating_difficulty": "Obliczanie...
Trudność:",
+ "js_speed": "Prędkość:",
+ "js_verification_longer": "Weryfikacja trwa dłużej niż zwykle. Proszę nie odświeżać strony.",
+ "js_success": "Sukces!",
+ "js_done_took": "Gotowe! Zajęło to",
+ "js_iterations": "iteracji",
+ "js_finished_reading": "Skończyłem czytać, kontynuuj →",
+ "js_calculation_error": "Błąd obliczeń!",
+ "js_calculation_error_msg": "Nie udało się obliczyć zadania:"
+}
diff --git a/lib/localization/locales/ru.json b/lib/localization/locales/ru.json
index 632efd3d..f98e8e14 100644
--- a/lib/localization/locales/ru.json
+++ b/lib/localization/locales/ru.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Не удалось рассчитать задачу:",
"missing_required_forwarded_headers": "Отсутствуют требуемые заголовки X-Forwarded-*",
"simplified_explanation": "Это мера против ботов и вредоносных запросов, аналогичная CAPTCHA. Однако вместо того, чтобы вам приходилось работать самостоятельно, вашему браузеру дается задача вычисления, которую он должен решить, чтобы убедиться, что он является действительным клиентом. Эта концепция называется
Доказательство выполнения работы. Задача рассчитывается за несколько секунд, и вам предоставляется доступ к веб-сайту. Спасибо за понимание и терпение."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/sv.json b/lib/localization/locales/sv.json
index dbb18cca..96bff72a 100644
--- a/lib/localization/locales/sv.json
+++ b/lib/localization/locales/sv.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "Misslyckades att kalkylera utmaning:",
"missing_required_forwarded_headers": "Saknar nödvändiga X-Forwarded-* headers",
"simplified_explanation": "Detta är en åtgärd mot botar och skadliga förfrågningar som liknar en CAPTCHA. Men i stället för att du själv måste göra jobbet får din webbläsare en beräkningsuppgift som den måste lösa för att säkerställa att den är en giltig klient. Detta koncept kallas
Arbetsbevis. Uppgiften beräknas på några sekunder och du beviljas tillgång till webbplatsen. Tack för din förståelse och ditt tålamod."
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/vi.json b/lib/localization/locales/vi.json
index edaf20fa..533212b2 100644
--- a/lib/localization/locales/vi.json
+++ b/lib/localization/locales/vi.json
@@ -63,4 +63,4 @@
"js_finished_reading": "Tôi đã đọc xong, tiếp tục →",
"js_calculation_error": "Lỗi tính toán!",
"js_calculation_error_msg": "Không thể tính toán thử thách:"
-}
\ No newline at end of file
+}
diff --git a/lib/localization/locales/zh-TW.json b/lib/localization/locales/zh-TW.json
index 4e7da25c..9f6403b6 100644
--- a/lib/localization/locales/zh-TW.json
+++ b/lib/localization/locales/zh-TW.json
@@ -63,4 +63,4 @@
"js_calculation_error_msg": "計算挑戰失敗:",
"missing_required_forwarded_headers": "缺少必要的 X-Forwarded-* 標頭",
"simplified_explanation": "這是一種類似於驗證碼的措施,用於防止機器人和惡意請求。但是,您無需自己動手,您的瀏覽器會收到一個計算任務,必須解決該任務以確保它是有效的客戶端。這個概念稱為
工作量證明。該任務在幾秒鐘內計算完畢,您將被授予訪問網站的權限。感謝您的理解和耐心。"
-}
\ No newline at end of file
+}
diff --git a/lib/policy/celchecker_test.go b/lib/policy/celchecker_test.go
new file mode 100644
index 00000000..61c90819
--- /dev/null
+++ b/lib/policy/celchecker_test.go
@@ -0,0 +1,44 @@
+package policy
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/TecharoHQ/anubis/internal/dns"
+ "github.com/TecharoHQ/anubis/lib/config"
+ "github.com/TecharoHQ/anubis/lib/store/memory"
+)
+
+func newTestDNS(t *testing.T) *dns.Dns {
+ t.Helper()
+
+ ctx := t.Context()
+ memStore := memory.New(ctx)
+ cache := dns.NewDNSCache(300, 300, memStore)
+ return dns.New(ctx, cache)
+}
+
+func TestCELChecker_MapIterationWrappers(t *testing.T) {
+ cfg := &config.ExpressionOrList{
+ Expression: `headers.exists(k, k == "Accept") && query.exists(k, k == "format")`,
+ }
+
+ checker, err := NewCELChecker(cfg, newTestDNS(t))
+ if err != nil {
+ t.Fatalf("creating CEL checker failed: %v", err)
+ }
+
+ req, err := http.NewRequest(http.MethodGet, "https://example.com/?format=json", nil)
+ if err != nil {
+ t.Fatalf("making request failed: %v", err)
+ }
+ req.Header.Set("Accept", "application/json")
+
+ got, err := checker.Check(req)
+ if err != nil {
+ t.Fatalf("checking expression failed: %v", err)
+ }
+ if !got {
+ t.Fatal("expected expression to evaluate true")
+ }
+}
diff --git a/lib/policy/expressions/environment_test.go b/lib/policy/expressions/environment_test.go
index beb1c2c6..c33fbbcb 100644
--- a/lib/policy/expressions/environment_test.go
+++ b/lib/policy/expressions/environment_test.go
@@ -103,7 +103,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{
+ result, _, err := prog.Eval(map[string]any{
"headers": tt.headers,
})
if err != nil {
@@ -168,7 +168,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{
+ result, _, err := prog.Eval(map[string]any{
"path": tt.path,
})
if err != nil {
@@ -280,7 +280,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{})
+ result, _, err := prog.Eval(map[string]any{})
if err != nil {
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
}
@@ -359,7 +359,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{})
+ result, _, err := prog.Eval(map[string]any{})
if err != nil {
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
}
@@ -421,7 +421,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{})
+ result, _, err := prog.Eval(map[string]any{})
if err != nil {
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
}
@@ -514,7 +514,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{})
+ result, _, err := prog.Eval(map[string]any{})
if err != nil {
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
}
@@ -572,7 +572,7 @@ func TestBotEnvironment(t *testing.T) {
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
}
- result, _, err := prog.Eval(map[string]interface{}{})
+ result, _, err := prog.Eval(map[string]any{})
if tt.evalError {
if err == nil {
t.Errorf("%s: expected an evaluation error, but got none", tt.description)
@@ -598,7 +598,7 @@ func TestThresholdEnvironment(t *testing.T) {
}
tests := []struct {
- variables map[string]interface{}
+ variables map[string]any
name string
expression string
description string
@@ -608,7 +608,7 @@ func TestThresholdEnvironment(t *testing.T) {
{
name: "weight-variable-available",
expression: `weight > 100`,
- variables: map[string]interface{}{"weight": 150},
+ variables: map[string]any{"weight": 150},
expected: types.Bool(true),
description: "should support weight variable in expressions",
shouldCompile: true,
@@ -616,7 +616,7 @@ func TestThresholdEnvironment(t *testing.T) {
{
name: "weight-variable-false-case",
expression: `weight > 100`,
- variables: map[string]interface{}{"weight": 50},
+ variables: map[string]any{"weight": 50},
expected: types.Bool(false),
description: "should correctly evaluate weight comparisons",
shouldCompile: true,
@@ -624,7 +624,7 @@ func TestThresholdEnvironment(t *testing.T) {
{
name: "missingHeader-not-available",
expression: `missingHeader(headers, "Test")`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expected: types.Bool(false), // not used
description: "should not have missingHeader function available",
shouldCompile: false,
@@ -667,7 +667,7 @@ func TestNewEnvironment(t *testing.T) {
tests := []struct {
name string
expression string
- variables map[string]interface{}
+ variables map[string]any
expectBool *bool // nil if we just want to test compilation or non-bool result
description string
shouldCompile bool
@@ -675,7 +675,7 @@ func TestNewEnvironment(t *testing.T) {
{
name: "randInt-function-compilation",
expression: `randInt(10)`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expectBool: nil, // Don't check result, just compilation
description: "should compile randInt function",
shouldCompile: true,
@@ -683,7 +683,7 @@ func TestNewEnvironment(t *testing.T) {
{
name: "randInt-range-validation",
expression: `randInt(10) >= 0 && randInt(10) < 10`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expectBool: boolPtr(true),
description: "should return values in correct range",
shouldCompile: true,
@@ -691,7 +691,7 @@ func TestNewEnvironment(t *testing.T) {
{
name: "strings-extension-size",
expression: `"hello".size() == 5`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expectBool: boolPtr(true),
description: "should support string extension functions",
shouldCompile: true,
@@ -699,7 +699,7 @@ func TestNewEnvironment(t *testing.T) {
{
name: "strings-extension-contains",
expression: `"hello world".contains("world")`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expectBool: boolPtr(true),
description: "should support string contains function",
shouldCompile: true,
@@ -707,7 +707,7 @@ func TestNewEnvironment(t *testing.T) {
{
name: "strings-extension-startsWith",
expression: `"hello world".startsWith("hello")`,
- variables: map[string]interface{}{},
+ variables: map[string]any{},
expectBool: boolPtr(true),
description: "should support string startsWith function",
shouldCompile: true,
diff --git a/lib/policy/expressions/http_headers.go b/lib/policy/expressions/http_headers.go
index 57fcc841..c4342dc3 100644
--- a/lib/policy/expressions/http_headers.go
+++ b/lib/policy/expressions/http_headers.go
@@ -66,7 +66,9 @@ func (h HTTPHeaders) Get(key ref.Val) ref.Val {
return result
}
-func (h HTTPHeaders) Iterator() traits.Iterator { panic("TODO(Xe): implement me") }
+func (h HTTPHeaders) Iterator() traits.Iterator {
+ return newMapIterator(h.Header)
+}
func (h HTTPHeaders) IsZeroValue() bool {
return len(h.Header) == 0
diff --git a/lib/policy/expressions/map_iterator.go b/lib/policy/expressions/map_iterator.go
new file mode 100644
index 00000000..4a86269f
--- /dev/null
+++ b/lib/policy/expressions/map_iterator.go
@@ -0,0 +1,60 @@
+package expressions
+
+import (
+ "errors"
+ "maps"
+ "reflect"
+ "slices"
+
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+ "github.com/google/cel-go/common/types/traits"
+)
+
+var ErrNotImplemented = errors.New("expressions: not implemented")
+
+type stringSliceIterator struct {
+ keys []string
+ idx int
+}
+
+func (s *stringSliceIterator) Value() any {
+ return s
+}
+
+func (s *stringSliceIterator) ConvertToNative(typeDesc reflect.Type) (any, error) {
+ return nil, ErrNotImplemented
+}
+
+func (s *stringSliceIterator) ConvertToType(typeValue ref.Type) ref.Val {
+ return types.NewErr("can't convert from %q to %q", types.IteratorType, typeValue)
+}
+
+func (s *stringSliceIterator) Equal(other ref.Val) ref.Val {
+ return types.NewErr("can't compare %q to %q", types.IteratorType, other.Type())
+}
+
+func (s *stringSliceIterator) Type() ref.Type {
+ return types.IteratorType
+}
+
+func (s *stringSliceIterator) HasNext() ref.Val {
+ return types.Bool(s.idx < len(s.keys))
+}
+
+func (s *stringSliceIterator) Next() ref.Val {
+ if s.HasNext() != types.True {
+ return nil
+ }
+
+ val := s.keys[s.idx]
+ s.idx++
+ return types.String(val)
+}
+
+func newMapIterator(m map[string][]string) traits.Iterator {
+ return &stringSliceIterator{
+ keys: slices.Collect(maps.Keys(m)),
+ idx: 0,
+ }
+}
diff --git a/lib/policy/expressions/url_values.go b/lib/policy/expressions/url_values.go
index a4c63519..21d25d5b 100644
--- a/lib/policy/expressions/url_values.go
+++ b/lib/policy/expressions/url_values.go
@@ -1,7 +1,6 @@
package expressions
import (
- "errors"
"net/url"
"reflect"
"strings"
@@ -11,8 +10,6 @@ import (
"github.com/google/cel-go/common/types/traits"
)
-var ErrNotImplemented = errors.New("expressions: not implemented")
-
// URLValues is a type wrapper to expose url.Values into CEL programs.
type URLValues struct {
url.Values
@@ -69,7 +66,9 @@ func (u URLValues) Get(key ref.Val) ref.Val {
return result
}
-func (u URLValues) Iterator() traits.Iterator { panic("TODO(Xe): implement me") }
+func (u URLValues) Iterator() traits.Iterator {
+ return newMapIterator(u.Values)
+}
func (u URLValues) IsZeroValue() bool {
return len(u.Values) == 0
diff --git a/lib/policy/policy_test.go b/lib/policy/policy_test.go
index 56ce3731..7f14d3f9 100644
--- a/lib/policy/policy_test.go
+++ b/lib/policy/policy_test.go
@@ -32,7 +32,6 @@ func TestGoodConfigs(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
t.Run("with-thoth", func(t *testing.T) {
fin, err := os.Open(filepath.Join("..", "config", "testdata", "good", st.Name()))
@@ -71,7 +70,6 @@ func TestBadConfigs(t *testing.T) {
}
for _, st := range finfos {
- st := st
t.Run(st.Name(), func(t *testing.T) {
fin, err := os.Open(filepath.Join("..", "config", "testdata", "bad", st.Name()))
if err != nil {
diff --git a/lib/policy/testdata/hack-test.json b/lib/policy/testdata/hack-test.json
index 652dcd87..a173bb18 100644
--- a/lib/policy/testdata/hack-test.json
+++ b/lib/policy/testdata/hack-test.json
@@ -2,8 +2,6 @@
{
"name": "ipv6-ula",
"action": "ALLOW",
- "remote_addresses": [
- "fc00::/7"
- ]
+ "remote_addresses": ["fc00::/7"]
}
-]
\ No newline at end of file
+]
diff --git a/lib/policy/testdata/hack-test.yaml b/lib/policy/testdata/hack-test.yaml
index cd4d7d05..29263e96 100644
--- a/lib/policy/testdata/hack-test.yaml
+++ b/lib/policy/testdata/hack-test.yaml
@@ -1,3 +1,3 @@
- name: well-known
path_regex: ^/.well-known/.*$
- action: ALLOW
\ No newline at end of file
+ action: ALLOW
diff --git a/lib/store/s3api/s3api_test.go b/lib/store/s3api/s3api_test.go
index 5b7a9c6b..37d58a76 100644
--- a/lib/store/s3api/s3api_test.go
+++ b/lib/store/s3api/s3api_test.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "maps"
"sync"
"testing"
"time"
@@ -36,9 +37,7 @@ func (m *mockS3) PutObject(ctx context.Context, in *s3.PutObjectInput, _ ...func
m.data[aws.ToString(in.Key)] = bytes.Clone(b)
if in.Metadata != nil {
m.meta[aws.ToString(in.Key)] = map[string]string{}
- for k, v := range in.Metadata {
- m.meta[aws.ToString(in.Key)][k] = v
- }
+ maps.Copy(m.meta[aws.ToString(in.Key)], in.Metadata)
}
m.bucket = aws.ToString(in.Bucket)
return &s3.PutObjectOutput{}, nil
diff --git a/lib/store/valkey/factory.go b/lib/store/valkey/factory.go
index 309bf656..49a63469 100644
--- a/lib/store/valkey/factory.go
+++ b/lib/store/valkey/factory.go
@@ -103,7 +103,7 @@ func (s Sentinel) Valid() error {
// redisClient is satisfied by *valkey.Client and *valkey.ClusterClient.
type redisClient interface {
Get(ctx context.Context, key string) *valkey.StringCmd
- Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *valkey.StatusCmd
+ Set(ctx context.Context, key string, value any, expiration time.Duration) *valkey.StatusCmd
Del(ctx context.Context, keys ...string) *valkey.IntCmd
Ping(ctx context.Context) *valkey.StatusCmd
}
diff --git a/lib/testdata/cloudflare-workers-cel.yaml b/lib/testdata/cloudflare-workers-cel.yaml
index b2d6de5a..b165d503 100644
--- a/lib/testdata/cloudflare-workers-cel.yaml
+++ b/lib/testdata/cloudflare-workers-cel.yaml
@@ -1,8 +1,8 @@
bots:
-- name: cloudflare-workers
- expression: '"Cf-Worker" in headers'
- action: DENY
+ - name: cloudflare-workers
+ expression: '"Cf-Worker" in headers'
+ action: DENY
status_codes:
CHALLENGE: 401
- DENY: 403
\ No newline at end of file
+ DENY: 403
diff --git a/lib/testdata/cloudflare-workers-header.yaml b/lib/testdata/cloudflare-workers-header.yaml
index 4fa9671b..20306445 100644
--- a/lib/testdata/cloudflare-workers-header.yaml
+++ b/lib/testdata/cloudflare-workers-header.yaml
@@ -1,9 +1,9 @@
bots:
-- name: cloudflare-workers
- headers_regex:
- CF-Worker: .*
- action: DENY
+ - name: cloudflare-workers
+ headers_regex:
+ CF-Worker: .*
+ action: DENY
status_codes:
CHALLENGE: 401
- DENY: 403
\ No newline at end of file
+ DENY: 403
diff --git a/lib/testdata/hack-test.json b/lib/testdata/hack-test.json
index 652dcd87..a173bb18 100644
--- a/lib/testdata/hack-test.json
+++ b/lib/testdata/hack-test.json
@@ -2,8 +2,6 @@
{
"name": "ipv6-ula",
"action": "ALLOW",
- "remote_addresses": [
- "fc00::/7"
- ]
+ "remote_addresses": ["fc00::/7"]
}
-]
\ No newline at end of file
+]
diff --git a/lib/testdata/hack-test.yaml b/lib/testdata/hack-test.yaml
index cd4d7d05..29263e96 100644
--- a/lib/testdata/hack-test.yaml
+++ b/lib/testdata/hack-test.yaml
@@ -1,3 +1,3 @@
- name: well-known
path_regex: ^/.well-known/.*$
- action: ALLOW
\ No newline at end of file
+ action: ALLOW
diff --git a/lib/testdata/rule_change.yaml b/lib/testdata/rule_change.yaml
index c4fe462d..737500e7 100644
--- a/lib/testdata/rule_change.yaml
+++ b/lib/testdata/rule_change.yaml
@@ -1,12 +1,12 @@
bots:
-- name: old-rule
- path_regex: ^/old$
- action: CHALLENGE
+ - name: old-rule
+ path_regex: ^/old$
+ action: CHALLENGE
-- name: new-rule
- path_regex: ^/new$
- action: CHALLENGE
+ - name: new-rule
+ path_regex: ^/new$
+ action: CHALLENGE
status_codes:
CHALLENGE: 401
- DENY: 403
\ No newline at end of file
+ DENY: 403
diff --git a/lib/testdata/useragent.yaml b/lib/testdata/useragent.yaml
new file mode 100644
index 00000000..85cf73e2
--- /dev/null
+++ b/lib/testdata/useragent.yaml
@@ -0,0 +1,12 @@
+bots:
+ - name: deny
+ user_agent_regex: DENY
+ action: DENY
+
+ - name: challenge
+ user_agent_regex: CHALLENGE
+ action: CHALLENGE
+
+ - name: allow
+ user_agent_regex: ALLOW
+ action: ALLOW
diff --git a/lib/thoth/auth.go b/lib/thoth/auth.go
index f4d52c6d..5f7d0c91 100644
--- a/lib/thoth/auth.go
+++ b/lib/thoth/auth.go
@@ -11,8 +11,8 @@ func authUnaryClientInterceptor(token string) grpc.UnaryClientInterceptor {
return func(
ctx context.Context,
method string,
- req interface{},
- reply interface{},
+ req any,
+ reply any,
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
diff --git a/package-lock.json b/package-lock.json
index c688b3bb..c548a7d5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,26 +1,31 @@
{
"name": "@techaro/anubis",
- "version": "1.24.0",
+ "version": "1.25.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@techaro/anubis",
- "version": "1.24.0",
+ "version": "1.25.0",
"license": "ISC",
"dependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
- "preact": "^10.28.1"
+ "preact": "^10.28.4"
},
"devDependencies": {
- "cssnano": "^7.1.2",
- "cssnano-preset-advanced": "^7.0.10",
- "esbuild": "^0.27.2",
+ "@commitlint/cli": "^20.4.3",
+ "@commitlint/config-conventional": "^20.4.3",
+ "baseline-browser-mapping": "^2.10.0",
+ "cssnano": "^7.1.3",
+ "cssnano-preset-advanced": "^7.0.11",
+ "esbuild": "^0.27.3",
+ "husky": "^9.1.7",
"playwright": "^1.52.0",
"postcss-cli": "^11.0.1",
"postcss-import": "^16.1.1",
"postcss-import-url": "^7.2.0",
- "postcss-url": "^10.1.3"
+ "postcss-url": "^10.1.3",
+ "prettier": "^3.8.1"
}
},
"node_modules/@aws-crypto/sha256-js": {
@@ -61,10 +66,344 @@
"node": ">=18.0.0"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@commitlint/cli": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.3.tgz",
+ "integrity": "sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/format": "^20.4.3",
+ "@commitlint/lint": "^20.4.3",
+ "@commitlint/load": "^20.4.3",
+ "@commitlint/read": "^20.4.3",
+ "@commitlint/types": "^20.4.3",
+ "tinyexec": "^1.0.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-conventional": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.3.tgz",
+ "integrity": "sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "conventional-changelog-conventionalcommits": "^9.2.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz",
+ "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.5.0",
+ "ajv": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/ensure": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz",
+ "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.5.0",
+ "lodash.camelcase": "^4.3.0",
+ "lodash.kebabcase": "^4.1.1",
+ "lodash.snakecase": "^4.1.1",
+ "lodash.startcase": "^4.4.0",
+ "lodash.upperfirst": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/execute-rule": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz",
+ "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/format": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz",
+ "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.5.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz",
+ "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.5.0",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@commitlint/lint": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz",
+ "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/is-ignored": "^20.5.0",
+ "@commitlint/parse": "^20.5.0",
+ "@commitlint/rules": "^20.5.0",
+ "@commitlint/types": "^20.5.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/load": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.0.tgz",
+ "integrity": "sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^20.5.0",
+ "@commitlint/execute-rule": "^20.0.0",
+ "@commitlint/resolve-extends": "^20.5.0",
+ "@commitlint/types": "^20.5.0",
+ "cosmiconfig": "^9.0.1",
+ "cosmiconfig-typescript-loader": "^6.1.0",
+ "is-plain-obj": "^4.1.0",
+ "lodash.mergewith": "^4.6.2",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/message": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz",
+ "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/parse": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz",
+ "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.5.0",
+ "conventional-changelog-angular": "^8.2.0",
+ "conventional-commits-parser": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/read": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz",
+ "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/top-level": "^20.4.3",
+ "@commitlint/types": "^20.5.0",
+ "git-raw-commits": "^5.0.0",
+ "minimist": "^1.2.8",
+ "tinyexec": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/resolve-extends": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.0.tgz",
+ "integrity": "sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^20.5.0",
+ "@commitlint/types": "^20.5.0",
+ "global-directory": "^4.0.1",
+ "import-meta-resolve": "^4.0.0",
+ "lodash.mergewith": "^4.6.2",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/rules": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz",
+ "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/ensure": "^20.5.0",
+ "@commitlint/message": "^20.4.3",
+ "@commitlint/to-lines": "^20.0.0",
+ "@commitlint/types": "^20.5.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/to-lines": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz",
+ "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/top-level": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz",
+ "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/types": {
+ "version": "20.5.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz",
+ "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "conventional-commits-parser": "^6.3.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@conventional-changelog/git-client": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.6.0.tgz",
+ "integrity": "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@simple-libs/child-process-utils": "^1.0.0",
+ "@simple-libs/stream-utils": "^1.2.0",
+ "semver": "^7.5.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "conventional-commits-filter": "^5.0.0",
+ "conventional-commits-parser": "^6.3.0"
+ },
+ "peerDependenciesMeta": {
+ "conventional-commits-filter": {
+ "optional": true
+ },
+ "conventional-commits-parser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@conventional-changelog/git-client/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
- "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"cpu": [
"ppc64"
],
@@ -79,9 +418,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
- "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [
"arm"
],
@@ -96,9 +435,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
- "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [
"arm64"
],
@@ -113,9 +452,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
- "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [
"x64"
],
@@ -130,9 +469,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
- "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [
"arm64"
],
@@ -147,9 +486,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
- "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [
"x64"
],
@@ -164,9 +503,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
- "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [
"arm64"
],
@@ -181,9 +520,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
- "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [
"x64"
],
@@ -198,9 +537,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
- "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [
"arm"
],
@@ -215,9 +554,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
- "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [
"arm64"
],
@@ -232,9 +571,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
- "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [
"ia32"
],
@@ -249,9 +588,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
- "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [
"loong64"
],
@@ -266,9 +605,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
- "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [
"mips64el"
],
@@ -283,9 +622,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
- "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [
"ppc64"
],
@@ -300,9 +639,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
- "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [
"riscv64"
],
@@ -317,9 +656,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
- "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [
"s390x"
],
@@ -334,9 +673,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
- "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [
"x64"
],
@@ -351,9 +690,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"cpu": [
"arm64"
],
@@ -368,9 +707,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
- "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [
"x64"
],
@@ -385,9 +724,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"cpu": [
"arm64"
],
@@ -402,9 +741,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
- "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [
"x64"
],
@@ -419,9 +758,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
- "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"cpu": [
"arm64"
],
@@ -436,9 +775,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
- "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [
"x64"
],
@@ -453,9 +792,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
- "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [
"arm64"
],
@@ -470,9 +809,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
- "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [
"ia32"
],
@@ -487,9 +826,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
- "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [
"x64"
],
@@ -503,6 +842,35 @@
"node": ">=18"
}
},
+ "node_modules/@simple-libs/child-process-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz",
+ "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@simple-libs/stream-utils": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/dangreen"
+ }
+ },
+ "node_modules/@simple-libs/stream-utils": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz",
+ "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/dangreen"
+ }
+ },
"node_modules/@smithy/is-array-buffer": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
@@ -553,6 +921,34 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@types/node": {
+ "version": "25.5.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
+ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -593,10 +989,24 @@
"node": ">= 8"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-ify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/autoprefixer": {
- "version": "10.4.21",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
- "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
"dev": true,
"funding": [
{
@@ -614,10 +1024,9 @@
],
"license": "MIT",
"dependencies": {
- "browserslist": "^4.24.4",
- "caniuse-lite": "^1.0.30001702",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001774",
+ "fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
@@ -639,13 +1048,16 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.8.23",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.23.tgz",
- "integrity": "sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==",
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
+ "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "baseline-browser-mapping": "dist/cli.js"
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
"node_modules/binary-extensions": {
@@ -693,9 +1105,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.27.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
- "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
@@ -712,13 +1124,12 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
- "baseline-browser-mapping": "^2.8.19",
- "caniuse-lite": "^1.0.30001751",
- "electron-to-chromium": "^1.5.238",
- "node-releases": "^2.0.26",
- "update-browserslist-db": "^1.1.4"
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -727,6 +1138,16 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -741,9 +1162,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001753",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz",
- "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==",
+ "version": "1.0.30001779",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz",
+ "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==",
"dev": true,
"funding": [
{
@@ -838,6 +1259,17 @@
"node": ">=16"
}
},
+ "node_modules/compare-func": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+ "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -845,6 +1277,94 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/conventional-changelog-angular": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz",
+ "integrity": "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/conventional-changelog-conventionalcommits": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.0.tgz",
+ "integrity": "sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/conventional-commits-parser": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz",
+ "integrity": "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@simple-libs/stream-utils": "^1.2.0",
+ "meow": "^13.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "dist/cli/index.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
+ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz",
+ "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jiti": "^2.6.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "cosmiconfig": ">=9",
+ "typescript": ">=5"
+ }
+ },
"node_modules/css-declaration-sorter": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
@@ -876,14 +1396,14 @@
}
},
"node_modules/css-tree": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
- "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+ "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "mdn-data": "2.12.2",
- "source-map-js": "^1.0.1"
+ "mdn-data": "2.27.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
@@ -916,13 +1436,13 @@
}
},
"node_modules/cssnano": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.2.tgz",
- "integrity": "sha512-HYOPBsNvoiFeR1eghKD5C3ASm64v9YVyJB4Ivnl2gqKoQYvjjN/G0rztvKQq8OxocUtC6sjqY8jwYngIB4AByA==",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.3.tgz",
+ "integrity": "sha512-mLFHQAzyapMVFLiJIn7Ef4C2UCEvtlTlbyILR6B5ZsUAV3D/Pa761R5uC1YPhyBkRd3eqaDm2ncaNrD7R4mTRg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "cssnano-preset-default": "^7.0.10",
+ "cssnano-preset-default": "^7.0.11",
"lilconfig": "^3.1.3"
},
"engines": {
@@ -937,16 +1457,16 @@
}
},
"node_modules/cssnano-preset-advanced": {
- "version": "7.0.10",
- "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.10.tgz",
- "integrity": "sha512-lfsKxX4H6WS7BbNyDxkGOu2VgN4bbHQpY8llA3i3SJ9ozAPJ1MHq265Aw0aslM161qiS0zhCHaC6zRcEbNAgUA==",
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.11.tgz",
+ "integrity": "sha512-k9Dz/8TBOmLojoMZ+2xwrGjscU1XwYVDVB9T1AmvZcwiWz4ibWm8/y+/SN5jhYcpMPTeDe7FhI2uEYlNq1iDGg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "autoprefixer": "^10.4.21",
- "browserslist": "^4.27.0",
- "cssnano-preset-default": "^7.0.10",
- "postcss-discard-unused": "^7.0.4",
+ "autoprefixer": "^10.4.27",
+ "browserslist": "^4.28.1",
+ "cssnano-preset-default": "^7.0.11",
+ "postcss-discard-unused": "^7.0.5",
"postcss-merge-idents": "^7.0.1",
"postcss-reduce-idents": "^7.0.1",
"postcss-zindex": "^7.0.1"
@@ -959,42 +1479,42 @@
}
},
"node_modules/cssnano-preset-default": {
- "version": "7.0.10",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.10.tgz",
- "integrity": "sha512-6ZBjW0Lf1K1Z+0OKUAUpEN62tSXmYChXWi2NAA0afxEVsj9a+MbcB1l5qel6BHJHmULai2fCGRthCeKSFbScpA==",
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.11.tgz",
+ "integrity": "sha512-waWlAMuCakP7//UCY+JPrQS1z0OSLeOXk2sKWJximKWGupVxre50bzPlvpbUwZIDylhf/ptf0Pk+Yf7C+hoa3g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"css-declaration-sorter": "^7.2.0",
"cssnano-utils": "^5.0.1",
"postcss-calc": "^10.1.1",
- "postcss-colormin": "^7.0.5",
- "postcss-convert-values": "^7.0.8",
- "postcss-discard-comments": "^7.0.5",
+ "postcss-colormin": "^7.0.6",
+ "postcss-convert-values": "^7.0.9",
+ "postcss-discard-comments": "^7.0.6",
"postcss-discard-duplicates": "^7.0.2",
"postcss-discard-empty": "^7.0.1",
"postcss-discard-overridden": "^7.0.1",
"postcss-merge-longhand": "^7.0.5",
- "postcss-merge-rules": "^7.0.7",
+ "postcss-merge-rules": "^7.0.8",
"postcss-minify-font-values": "^7.0.1",
"postcss-minify-gradients": "^7.0.1",
- "postcss-minify-params": "^7.0.5",
- "postcss-minify-selectors": "^7.0.5",
+ "postcss-minify-params": "^7.0.6",
+ "postcss-minify-selectors": "^7.0.6",
"postcss-normalize-charset": "^7.0.1",
"postcss-normalize-display-values": "^7.0.1",
"postcss-normalize-positions": "^7.0.1",
"postcss-normalize-repeat-style": "^7.0.1",
"postcss-normalize-string": "^7.0.1",
"postcss-normalize-timing-functions": "^7.0.1",
- "postcss-normalize-unicode": "^7.0.5",
+ "postcss-normalize-unicode": "^7.0.6",
"postcss-normalize-url": "^7.0.1",
"postcss-normalize-whitespace": "^7.0.1",
"postcss-ordered-values": "^7.0.2",
- "postcss-reduce-initial": "^7.0.5",
+ "postcss-reduce-initial": "^7.0.6",
"postcss-reduce-transforms": "^7.0.1",
- "postcss-svgo": "^7.1.0",
- "postcss-unique-selectors": "^7.0.4"
+ "postcss-svgo": "^7.1.1",
+ "postcss-unique-selectors": "^7.0.5"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -1128,10 +1648,23 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/electron-to-chromium": {
- "version": "1.5.244",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz",
- "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==",
+ "version": "1.5.313",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz",
+ "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==",
"dev": true,
"license": "ISC"
},
@@ -1155,10 +1688,30 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/esbuild": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
- "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1169,32 +1722,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.2",
- "@esbuild/android-arm": "0.27.2",
- "@esbuild/android-arm64": "0.27.2",
- "@esbuild/android-x64": "0.27.2",
- "@esbuild/darwin-arm64": "0.27.2",
- "@esbuild/darwin-x64": "0.27.2",
- "@esbuild/freebsd-arm64": "0.27.2",
- "@esbuild/freebsd-x64": "0.27.2",
- "@esbuild/linux-arm": "0.27.2",
- "@esbuild/linux-arm64": "0.27.2",
- "@esbuild/linux-ia32": "0.27.2",
- "@esbuild/linux-loong64": "0.27.2",
- "@esbuild/linux-mips64el": "0.27.2",
- "@esbuild/linux-ppc64": "0.27.2",
- "@esbuild/linux-riscv64": "0.27.2",
- "@esbuild/linux-s390x": "0.27.2",
- "@esbuild/linux-x64": "0.27.2",
- "@esbuild/netbsd-arm64": "0.27.2",
- "@esbuild/netbsd-x64": "0.27.2",
- "@esbuild/openbsd-arm64": "0.27.2",
- "@esbuild/openbsd-x64": "0.27.2",
- "@esbuild/openharmony-arm64": "0.27.2",
- "@esbuild/sunos-x64": "0.27.2",
- "@esbuild/win32-arm64": "0.27.2",
- "@esbuild/win32-ia32": "0.27.2",
- "@esbuild/win32-x64": "0.27.2"
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
}
},
"node_modules/escalade": {
@@ -1207,6 +1760,30 @@
"node": ">=6"
}
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -1221,16 +1798,16 @@
}
},
"node_modules/fraction.js": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
- "type": "patreon",
+ "type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
@@ -1284,6 +1861,23 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/git-raw-commits": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz",
+ "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@conventional-changelog/git-client": "^2.6.0",
+ "meow": "^13.0.0"
+ },
+ "bin": {
+ "git-raw-commits": "src/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -1297,6 +1891,22 @@
"node": ">= 6"
}
},
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -1324,6 +1934,77 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ini": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -1396,6 +2077,29 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
@@ -1403,6 +2107,50 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/jsonfile": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
@@ -1429,6 +2177,13 @@
"url": "https://github.com/sponsors/antonk52"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
@@ -1436,6 +2191,20 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.kebabcase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+ "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -1443,6 +2212,27 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.startcase": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+ "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.trim": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
@@ -1457,6 +2247,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.upperfirst": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+ "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -1474,12 +2271,25 @@
}
},
"node_modules/mdn-data": {
- "version": "2.12.2",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
- "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+ "version": "2.27.1",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+ "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
"dev": true,
"license": "CC0-1.0"
},
+ "node_modules/meow": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+ "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mime": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
@@ -1506,6 +2316,16 @@
"node": "*"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -1518,6 +2338,7 @@
}
],
"license": "MIT",
+ "peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -1542,16 +2363,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -1565,6 +2376,38 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -1711,13 +2554,13 @@
}
},
"node_modules/postcss-colormin": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.5.tgz",
- "integrity": "sha512-ekIBP/nwzRWhEMmIxHHbXHcMdzd1HIUzBECaj5KEdLz9DVP2HzT065sEhvOx1dkLjYW7jyD0CngThx6bpFi2fA==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.6.tgz",
+ "integrity": "sha512-oXM2mdx6IBTRm39797QguYzVEWzbdlFiMNfq88fCCN1Wepw3CYmJ/1/Ifa/KjWo+j5ZURDl2NTldLJIw51IeNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"caniuse-api": "^3.0.0",
"colord": "^2.9.3",
"postcss-value-parser": "^4.2.0"
@@ -1730,13 +2573,13 @@
}
},
"node_modules/postcss-convert-values": {
- "version": "7.0.8",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.8.tgz",
- "integrity": "sha512-+XNKuPfkHTCEo499VzLMYn94TiL3r9YqRE3Ty+jP7UX4qjewUONey1t7CG21lrlTLN07GtGM8MqFVp86D4uKJg==",
+ "version": "7.0.9",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.9.tgz",
+ "integrity": "sha512-l6uATQATZaCa0bckHV+r6dLXfWtUBKXxO3jK+AtxxJJtgMPD+VhhPCCx51I4/5w8U5uHV67g3w7PXj+V3wlMlg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@@ -1747,13 +2590,13 @@
}
},
"node_modules/postcss-discard-comments": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.5.tgz",
- "integrity": "sha512-IR2Eja8WfYgN5n32vEGSctVQ1+JARfu4UH8M7bgGh1bC+xI/obsPJXaBpQF7MAByvgwZinhpHpdrmXtvVVlKcQ==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.6.tgz",
+ "integrity": "sha512-Sq+Fzj1Eg5/CPf1ERb0wS1Im5cvE2gDXCE+si4HCn1sf+jpQZxDI4DXEp8t77B/ImzDceWE2ebJQFXdqZ6GRJw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "postcss-selector-parser": "^7.1.0"
+ "postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -1802,13 +2645,13 @@
}
},
"node_modules/postcss-discard-unused": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-7.0.4.tgz",
- "integrity": "sha512-/d6sIm8SSJbDDzdHyt/BWZ5upC6Dtn6JIL0uQts+AuvA5ddVmkw/3H4NtDv7DybGzCA1o3Q9R6kt4qsnS2mCSQ==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-7.0.5.tgz",
+ "integrity": "sha512-CdTIk/qGQHCY/v+FCTTUHVEIfNQv3xSDF+TudDC4l7JS1XaKk0gv0GM6heLF5pbt57l8/YQ4GsFwl2hYz2L9gA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "postcss-selector-parser": "^7.1.0"
+ "postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -1930,16 +2773,16 @@
}
},
"node_modules/postcss-merge-rules": {
- "version": "7.0.7",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.7.tgz",
- "integrity": "sha512-njWJrd/Ms6XViwowaaCc+/vqhPG3SmXn725AGrnl+BgTuRPEacjiLEaGq16J6XirMJbtKkTwnt67SS+e2WGoew==",
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.8.tgz",
+ "integrity": "sha512-BOR1iAM8jnr7zoQSlpeBmCsWV5Uudi/+5j7k05D0O/WP3+OFMPD86c1j/20xiuRtyt45bhxw/7hnhZNhW2mNFA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"caniuse-api": "^3.0.0",
"cssnano-utils": "^5.0.1",
- "postcss-selector-parser": "^7.1.0"
+ "postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -1983,13 +2826,13 @@
}
},
"node_modules/postcss-minify-params": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.5.tgz",
- "integrity": "sha512-FGK9ky02h6Ighn3UihsyeAH5XmLEE2MSGH5Tc4tXMFtEDx7B+zTG6hD/+/cT+fbF7PbYojsmmWjyTwFwW1JKQQ==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.6.tgz",
+ "integrity": "sha512-YOn02gC68JijlaXVuKvFSCvQOhTpblkcfDre2hb/Aaa58r2BIaK4AtE/cyZf2wV7YKAG+UlP9DT+By0ry1E4VQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0"
},
@@ -2001,14 +2844,14 @@
}
},
"node_modules/postcss-minify-selectors": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz",
- "integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.6.tgz",
+ "integrity": "sha512-lIbC0jy3AAwDxEgciZlBullDiMBeBCT+fz5G8RcA9MWqh/hfUkpOI3vNDUNEZHgokaoiv0juB9Y8fGcON7rU/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
- "postcss-selector-parser": "^7.1.0"
+ "postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -2111,13 +2954,13 @@
}
},
"node_modules/postcss-normalize-unicode": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.5.tgz",
- "integrity": "sha512-X6BBwiRxVaFHrb2WyBMddIeB5HBjJcAaUHyhLrM2FsxSq5TFqcHSsK7Zu1otag+o0ZphQGJewGH1tAyrD0zX1Q==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.6.tgz",
+ "integrity": "sha512-z6bwTV84YW6ZvvNoaNLuzRW4/uWxDKYI1iIDrzk6D2YTL7hICApy+Q1LP6vBEsljX8FM7YSuV9qI79XESd4ddQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@@ -2193,13 +3036,13 @@
}
},
"node_modules/postcss-reduce-initial": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.5.tgz",
- "integrity": "sha512-RHagHLidG8hTZcnr4FpyMB2jtgd/OcyAazjMhoy5qmWJOx1uxKh4ntk0Pb46ajKM0rkf32lRH4C8c9qQiPR6IA==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.6.tgz",
+ "integrity": "sha512-G6ZyK68AmrPdMB6wyeA37ejnnRG2S8xinJrZJnOv+IaRKf6koPAVbQsiC7MfkmXaGmF1UO+QCijb27wfpxuRNg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.27.0",
+ "browserslist": "^4.28.1",
"caniuse-api": "^3.0.0"
},
"engines": {
@@ -2253,9 +3096,9 @@
}
},
"node_modules/postcss-selector-parser": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
- "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
+ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2267,14 +3110,14 @@
}
},
"node_modules/postcss-svgo": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz",
- "integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.1.tgz",
+ "integrity": "sha512-zU9H9oEDrUFKa0JB7w+IYL7Qs9ey1mZyjhbf0KLxwJDdDRtoPvCmaEfknzqfHj44QS9VD6c5sJnBAVYTLRg/Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0",
- "svgo": "^4.0.0"
+ "svgo": "^4.0.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >= 18"
@@ -2284,13 +3127,13 @@
}
},
"node_modules/postcss-unique-selectors": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz",
- "integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.5.tgz",
+ "integrity": "sha512-3QoYmEt4qg/rUWDn6Tc8+ZVPmbp4G1hXDtCNWDx0st8SjtCbRcxRXDDM1QrEiXGG3A45zscSJFb4QH90LViyxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "postcss-selector-parser": "^7.1.0"
+ "postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@@ -2339,15 +3182,31 @@
}
},
"node_modules/preact": {
- "version": "10.28.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.1.tgz",
- "integrity": "sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw==",
+ "version": "10.28.4",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.4.tgz",
+ "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-hrtime": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
@@ -2408,6 +3267,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -2429,6 +3298,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/resolve-relative-url": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-relative-url/-/resolve-relative-url-1.0.0.tgz",
@@ -2440,11 +3319,14 @@
}
},
"node_modules/sax": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
- "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
+ "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
"dev": true,
- "license": "ISC"
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
},
"node_modules/semver": {
"version": "6.3.1",
@@ -2538,9 +3420,9 @@
}
},
"node_modules/svgo": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
- "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
+ "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2550,7 +3432,7 @@
"css-what": "^6.1.0",
"csso": "^5.0.5",
"picocolors": "^1.1.1",
- "sax": "^1.4.1"
+ "sax": "^1.5.0"
},
"bin": {
"svgo": "bin/svgo.js"
@@ -2570,6 +3452,16 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/tinyexec": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz",
+ "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -2611,7 +3503,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -2638,6 +3529,29 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -2649,9 +3563,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
- "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
diff --git a/package.json b/package.json
index 89f86c6b..cdd08d27 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "@techaro/anubis",
- "version": "1.24.0",
+ "version": "1.25.0",
"description": "",
"main": "index.js",
"scripts": {
- "test": "npm run assets && go test ./...",
+ "test": "npm run assets && SKIP_INTEGRATION=1 go test ./...",
"test:integration": "npm run assets && go test -v ./internal/test",
"test:integration:podman": "npm run assets && go test -v ./internal/test --playwright-runner=podman",
"test:integration:docker": "npm run assets && go test -v ./internal/test --playwright-runner=docker",
@@ -12,23 +12,58 @@
"build": "npm run assets && go build -o ./var/anubis ./cmd/anubis",
"dev": "npm run assets && go run ./cmd/anubis --use-remote-address --target http://localhost:3000",
"container": "npm run assets && go run ./cmd/containerbuild",
- "package": "yeet",
- "lint": "make lint"
+ "package": "go tool yeet",
+ "lint": "make lint",
+ "prepare": "husky && go mod download",
+ "format": "prettier -w . 2>&1 >/dev/null && go run goimports -w ."
},
"author": "",
"license": "ISC",
"devDependencies": {
- "cssnano": "^7.1.2",
- "cssnano-preset-advanced": "^7.0.10",
- "esbuild": "^0.27.2",
+ "@commitlint/cli": "^20.4.3",
+ "@commitlint/config-conventional": "^20.4.3",
+ "baseline-browser-mapping": "^2.10.0",
+ "cssnano": "^7.1.3",
+ "cssnano-preset-advanced": "^7.0.11",
+ "esbuild": "^0.27.3",
+ "husky": "^9.1.7",
"playwright": "^1.52.0",
"postcss-cli": "^11.0.1",
"postcss-import": "^16.1.1",
"postcss-import-url": "^7.2.0",
- "postcss-url": "^10.1.3"
+ "postcss-url": "^10.1.3",
+ "prettier": "^3.8.1"
},
"dependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
- "preact": "^10.28.1"
+ "preact": "^10.28.4"
+ },
+ "commitlint": {
+ "extends": [
+ "@commitlint/config-conventional"
+ ],
+ "rules": {
+ "body-max-line-length": [
+ 2,
+ "always",
+ 99999
+ ],
+ "footer-max-line-length": [
+ 2,
+ "always",
+ 99999
+ ],
+ "signed-off-by": [
+ 2,
+ "always"
+ ]
+ }
+ },
+ "prettier": {
+ "singleQuote": false,
+ "tabWidth": 2,
+ "semi": true,
+ "trailingComma": "all",
+ "printWidth": 80
}
}
\ No newline at end of file
diff --git a/test/anubis_configs/aggressive_403.yaml b/test/anubis_configs/aggressive_403.yaml
index facafd6f..ff966517 100644
--- a/test/anubis_configs/aggressive_403.yaml
+++ b/test/anubis_configs/aggressive_403.yaml
@@ -1,12 +1,12 @@
bots:
-- name: deny
- user_agent_regex: DENY
- action: DENY
+ - name: deny
+ user_agent_regex: DENY
+ action: DENY
-- name: challenge
- user_agent_regex: CHALLENGE
- action: CHALLENGE
+ - name: challenge
+ user_agent_regex: CHALLENGE
+ action: CHALLENGE
status_codes:
CHALLENGE: 401
- DENY: 403
\ No newline at end of file
+ DENY: 403
diff --git a/test/double_slash/test.mjs b/test/double_slash/test.mjs
index 7ae9c5a8..15a54eb9 100644
--- a/test/double_slash/test.mjs
+++ b/test/double_slash/test.mjs
@@ -3,13 +3,13 @@ import { createInterface } from "readline";
async function getPage(path) {
return fetch(`http://localhost:8923${path}`)
- .then(resp => {
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.text());
+ .then((resp) => resp.text());
}
(async () => {
@@ -42,4 +42,4 @@ async function getPage(path) {
}
process.exit(failed ? 1 : 0);
-})();
\ No newline at end of file
+})();
diff --git a/test/forced-language/test.mjs b/test/forced-language/test.mjs
index e64237ef..7fab657e 100644
--- a/test/forced-language/test.mjs
+++ b/test/forced-language/test.mjs
@@ -3,25 +3,25 @@ async function getChallengePage() {
headers: {
"Accept-Language": "en",
"User-Agent": "CHALLENGE",
- }
+ },
})
- .then(resp => {
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.text());
+ .then((resp) => resp.text());
}
(async () => {
const page = await getChallengePage();
if (!page.includes(``)) {
- console.log(page)
+ console.log(page);
throw new Error("force language smoke test failed");
}
console.log("FORCED_LANGUAGE=de caused a page to be rendered in german");
process.exit(0);
-})();
\ No newline at end of file
+})();
diff --git a/test/i18n/test.mjs b/test/i18n/test.mjs
index 64b32703..ba48ea28 100644
--- a/test/i18n/test.mjs
+++ b/test/i18n/test.mjs
@@ -1,12 +1,14 @@
async function fetchLanguages() {
- return fetch("http://localhost:8923/.within.website/x/cmd/anubis/static/locales/manifest.json")
- .then(resp => {
+ return fetch(
+ "http://localhost:8923/.within.website/x/cmd/anubis/static/locales/manifest.json",
+ )
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.json());
+ .then((resp) => resp.json());
}
async function getChallengePage(lang) {
@@ -14,15 +16,15 @@ async function getChallengePage(lang) {
headers: {
"Accept-Language": lang,
"User-Agent": "CHALLENGE",
- }
+ },
})
- .then(resp => {
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.text());
+ .then((resp) => resp.text());
}
(async () => {
@@ -42,7 +44,7 @@ async function getChallengePage(lang) {
console.log(`getting for ${lang}`);
const page = await getChallengePage(lang);
- resultSheet[lang] = page.includes(``)
+ resultSheet[lang] = page.includes(``);
}
for (const [lang, result] of Object.entries(resultSheet)) {
@@ -59,4 +61,4 @@ async function getChallengePage(lang) {
}
process.exit(0);
-})();
\ No newline at end of file
+})();
diff --git a/test/k8s/cert-manager/selfsigned-issuer.yaml b/test/k8s/cert-manager/selfsigned-issuer.yaml
index 07d2b7bc..8120e82a 100644
--- a/test/k8s/cert-manager/selfsigned-issuer.yaml
+++ b/test/k8s/cert-manager/selfsigned-issuer.yaml
@@ -3,4 +3,4 @@ kind: ClusterIssuer
metadata:
name: selfsigned
spec:
- selfSigned: {}
\ No newline at end of file
+ selfSigned: {}
diff --git a/test/k8s/deps/cert-manager.yaml b/test/k8s/deps/cert-manager.yaml
index f3e17fae..1d3fda13 100644
--- a/test/k8s/deps/cert-manager.yaml
+++ b/test/k8s/deps/cert-manager.yaml
@@ -10,4 +10,4 @@ spec:
createNamespace: true
set:
installCRDs: "true"
- "prometheus.enabled": "false"
\ No newline at end of file
+ "prometheus.enabled": "false"
diff --git a/test/log-file/test.mjs b/test/log-file/test.mjs
index 8b036cd3..f9db6454 100644
--- a/test/log-file/test.mjs
+++ b/test/log-file/test.mjs
@@ -3,16 +3,16 @@ import { statSync } from "fs";
async function getPage(path) {
return fetch(`http://localhost:8923${path}`, {
headers: {
- 'User-Agent': 'CHALLENGE'
- }
+ "User-Agent": "CHALLENGE",
+ },
})
- .then(resp => {
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.text());
+ .then((resp) => resp.text());
}
async function getFileSize(filePath) {
@@ -63,7 +63,9 @@ async function getFileSize(filePath) {
// Verify that log file size increased
if (finalSize <= initialSize) {
- console.error("ERROR: Log file size did not increase after making requests!");
+ console.error(
+ "ERROR: Log file size did not increase after making requests!",
+ );
failed = true;
}
@@ -79,10 +81,14 @@ async function getFileSize(filePath) {
console.log(`Successful requests: ${successCount}/${requests.length}`);
if (failed) {
- console.error("Test failed: Some requests failed or log file size did not increase");
+ console.error(
+ "Test failed: Some requests failed or log file size did not increase",
+ );
process.exit(1);
} else {
- console.log("Test passed: All requests succeeded and log file size increased");
+ console.log(
+ "Test passed: All requests succeeded and log file size increased",
+ );
process.exit(0);
}
-})();
\ No newline at end of file
+})();
diff --git a/test/nginx/conf/nginx/conf-anubis.inc b/test/nginx/conf/nginx/conf-anubis.inc
index 6e5083ae..1535ec91 100644
--- a/test/nginx/conf/nginx/conf-anubis.inc
+++ b/test/nginx/conf/nginx/conf-anubis.inc
@@ -1,8 +1,7 @@
# /etc/nginx/conf-anubis.inc
-
# Forward to anubis
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://anubis;
-}
\ No newline at end of file
+}
diff --git a/test/robots_txt/test.mjs b/test/robots_txt/test.mjs
index ce62b9f7..faa6d89d 100644
--- a/test/robots_txt/test.mjs
+++ b/test/robots_txt/test.mjs
@@ -3,25 +3,25 @@ async function getRobotsTxt() {
headers: {
"Accept-Language": "en",
"User-Agent": "Mozilla/5.0",
- }
+ },
})
- .then(resp => {
+ .then((resp) => {
if (resp.status !== 200) {
throw new Error(`wanted status 200, got status: ${resp.status}`);
}
return resp;
})
- .then(resp => resp.text());
+ .then((resp) => resp.text());
}
(async () => {
const page = await getRobotsTxt();
if (page.includes(``)) {
- console.log(page)
+ console.log(page);
throw new Error("serve robots.txt smoke test failed");
}
console.log("serve-robots-txt serves robots.txt");
process.exit(0);
-})();
\ No newline at end of file
+})();
diff --git a/test/shared/www/index.html b/test/shared/www/index.html
index 8c55c8cb..f259424f 100644
--- a/test/shared/www/index.html
+++ b/test/shared/www/index.html
@@ -1,9 +1,9 @@
-
+
Anubis works!
-
-
+
+
@@ -11,7 +11,10 @@
If you see this, everything has gone according to keikaku.
-
+
-
\ No newline at end of file
+
diff --git a/test/unix-socket-xff/test.mjs b/test/unix-socket-xff/test.mjs
index 8b8479c0..5c85dba4 100644
--- a/test/unix-socket-xff/test.mjs
+++ b/test/unix-socket-xff/test.mjs
@@ -1,19 +1,20 @@
async function testWithUserAgent(userAgent) {
- const statusCode =
- await fetch("https://relayd.local.cetacean.club:3004/reqmeta", {
+ const statusCode = await fetch(
+ "https://relayd.local.cetacean.club:3004/reqmeta",
+ {
headers: {
"User-Agent": userAgent,
- }
- })
- .then(resp => resp.status);
+ },
+ },
+ ).then((resp) => resp.status);
return statusCode;
}
const codes = {
allow: await testWithUserAgent("ALLOW"),
challenge: await testWithUserAgent("CHALLENGE"),
- deny: await testWithUserAgent("DENY")
-}
+ deny: await testWithUserAgent("DENY"),
+};
const expected = {
allow: 200,
@@ -26,5 +27,7 @@ console.log("CHALLENGE:", codes.challenge);
console.log("DENY: ", codes.deny);
if (JSON.stringify(codes) !== JSON.stringify(expected)) {
- throw new Error(`wanted ${JSON.stringify(expected)}, got: ${JSON.stringify(codes)}`);
-}
\ No newline at end of file
+ throw new Error(
+ `wanted ${JSON.stringify(expected)}, got: ${JSON.stringify(codes)}`,
+ );
+}
diff --git a/web/index.templ b/web/index.templ
index 9ad02b3f..330b6fa8 100644
--- a/web/index.templ
+++ b/web/index.templ
@@ -40,16 +40,16 @@ templ base(title string, body templ.Component, impressum *config.Impressum, chal
}
#progress {
- display: none;
- width: 90%;
- width: min(20rem, 90%);
- height: 2rem;
- border-radius: 1rem;
- overflow: hidden;
- margin: 1rem 0 2rem;
- outline-offset: 2px;
- outline: #b16286 solid 4px;
- }
+ display: none;
+ width: 90%;
+ width: min(20rem, 90%);
+ height: 2rem;
+ border-radius: 1rem;
+ overflow: hidden;
+ margin: 1rem 0 2rem;
+ outline-offset: 2px;
+ outline: #b16286 solid 4px;
+ }
.bar-inner {
background-color: #b16286;
diff --git a/web/index_templ.go b/web/index_templ.go
index de0af4fc..2099c7b0 100644
--- a/web/index_templ.go
+++ b/web/index_templ.go
@@ -113,7 +113,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/web/js/algorithms/fast.ts b/web/js/algorithms/fast.ts
index 6330da56..82a9a778 100644
--- a/web/js/algorithms/fast.ts
+++ b/web/js/algorithms/fast.ts
@@ -6,7 +6,9 @@ interface ProcessOptions {
}
const getHardwareConcurrency = () =>
- navigator.hardwareConcurrency !== undefined ? navigator.hardwareConcurrency : 1;
+ navigator.hardwareConcurrency !== undefined
+ ? navigator.hardwareConcurrency
+ : 1;
export default function process(
options: ProcessOptions,
@@ -25,7 +27,10 @@ export default function process(
workerMethod = "webcrypto";
}
- if (navigator.userAgent.includes("Firefox") || navigator.userAgent.includes("Goanna")) {
+ if (
+ navigator.userAgent.includes("Firefox") ||
+ navigator.userAgent.includes("Goanna")
+ ) {
console.log("Firefox detected, using pure-JS fallback");
workerMethod = "purejs";
}
diff --git a/web/js/algorithms/index.ts b/web/js/algorithms/index.ts
index 5b571837..2781aef0 100644
--- a/web/js/algorithms/index.ts
+++ b/web/js/algorithms/index.ts
@@ -3,4 +3,4 @@ import fast from "./fast";
export default {
fast: fast,
slow: fast, // XXX(Xe): slow is deprecated, but keep this around in case anything goes bad
-}
\ No newline at end of file
+};
diff --git a/web/js/bench.ts b/web/js/bench.ts
index 719b1675..1dd7805c 100644
--- a/web/js/bench.ts
+++ b/web/js/bench.ts
@@ -2,13 +2,27 @@ import algorithms from "./algorithms";
const defaultDifficulty = 4;
-const status: HTMLParagraphElement = document.getElementById("status") as HTMLParagraphElement;
-const difficultyInput: HTMLInputElement = document.getElementById("difficulty-input") as HTMLInputElement;
-const algorithmSelect: HTMLSelectElement = document.getElementById("algorithm-select") as HTMLSelectElement;
-const compareSelect: HTMLSelectElement = document.getElementById("compare-select") as HTMLSelectElement;
-const header: HTMLTableRowElement = document.getElementById("table-header") as HTMLTableRowElement;
-const headerCompare: HTMLTableSectionElement = document.getElementById("table-header-compare") as HTMLTableSectionElement;
-const results: HTMLTableRowElement = document.getElementById("results") as HTMLTableRowElement;
+const status: HTMLParagraphElement = document.getElementById(
+ "status",
+) as HTMLParagraphElement;
+const difficultyInput: HTMLInputElement = document.getElementById(
+ "difficulty-input",
+) as HTMLInputElement;
+const algorithmSelect: HTMLSelectElement = document.getElementById(
+ "algorithm-select",
+) as HTMLSelectElement;
+const compareSelect: HTMLSelectElement = document.getElementById(
+ "compare-select",
+) as HTMLSelectElement;
+const header: HTMLTableRowElement = document.getElementById(
+ "table-header",
+) as HTMLTableRowElement;
+const headerCompare: HTMLTableSectionElement = document.getElementById(
+ "table-header-compare",
+) as HTMLTableSectionElement;
+const results: HTMLTableRowElement = document.getElementById(
+ "results",
+) as HTMLTableRowElement;
const setupControls = () => {
if (defaultDifficulty == null) {
@@ -41,7 +55,12 @@ const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
.join("");
const t0 = performance.now();
- const { hash, nonce } = await process({ basePrefix: "/", version: "devel" }, challenge, Number(difficulty), signal);
+ const { hash, nonce } = await process(
+ { basePrefix: "/", version: "devel" },
+ challenge,
+ Number(difficulty),
+ signal,
+ );
const t1 = performance.now();
console.log({ hash, nonce });
diff --git a/web/js/main.ts b/web/js/main.ts
index fbedb3a6..b07d52d9 100644
--- a/web/js/main.ts
+++ b/web/js/main.ts
@@ -29,22 +29,25 @@ const getAvailableLanguages = async () => {
}
try {
- const response = await fetch(`${basePrefix}/.within.website/x/cmd/anubis/static/locales/manifest.json`);
+ const response = await fetch(
+ `${basePrefix}/.within.website/x/cmd/anubis/static/locales/manifest.json`,
+ );
if (response.ok) {
const manifest = await response.json();
- return manifest.supportedLanguages || ['en'];
+ return manifest.supportedLanguages || ["en"];
}
} catch (error) {
- console.warn('Failed to load language manifest, falling back to default languages');
+ console.warn(
+ "Failed to load language manifest, falling back to default languages",
+ );
}
// Fallback to default languages if manifest loading fails
- return ['en'];
+ return ["en"];
};
// Use the browser language from the HTML lang attribute which is set by the server settings or request headers
-const getBrowserLanguage = async () =>
- document.documentElement.lang;
+const getBrowserLanguage = async () => document.documentElement.lang;
// Load translations from JSON files
const loadTranslations = async (lang) => {
@@ -54,12 +57,16 @@ const loadTranslations = async (lang) => {
}
try {
- const response = await fetch(`${basePrefix}/.within.website/x/cmd/anubis/static/locales/${lang}.json`);
+ const response = await fetch(
+ `${basePrefix}/.within.website/x/cmd/anubis/static/locales/${lang}.json`,
+ );
return await response.json();
} catch (error) {
- console.warn(`Failed to load translations for ${lang}, falling back to English`);
- if (lang !== 'en') {
- return await loadTranslations('en');
+ console.warn(
+ `Failed to load translations for ${lang}, falling back to English`,
+ );
+ if (lang !== "en") {
+ return await loadTranslations("en");
}
throw error;
}
@@ -72,10 +79,10 @@ const getRedirectUrl = () => {
}
if (publicUrl && window.location.href.startsWith(publicUrl)) {
const urlParams = new URLSearchParams(window.location.search);
- return urlParams.get('redir');
+ return urlParams.get("redir");
}
return window.location.href;
-}
+};
let translations = {};
let currentLang;
@@ -95,20 +102,28 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
const dependencies = [
{
name: "Web Workers",
- msg: t('web_workers_error'),
+ msg: t("web_workers_error"),
value: window.Worker,
},
{
name: "Cookies",
- msg: t('cookies_error'),
+ msg: t("cookies_error"),
value: navigator.cookieEnabled,
},
];
- const status: HTMLParagraphElement = document.getElementById("status") as HTMLParagraphElement;
- const image: HTMLImageElement = document.getElementById("image") as HTMLImageElement;
- const title: HTMLHeadingElement = document.getElementById("title") as HTMLHeadingElement;
- const progress: HTMLDivElement = document.getElementById("progress") as HTMLDivElement;
+ const status: HTMLParagraphElement = document.getElementById(
+ "status",
+ ) as HTMLParagraphElement;
+ const image: HTMLImageElement = document.getElementById(
+ "image",
+ ) as HTMLImageElement;
+ const title: HTMLHeadingElement = document.getElementById(
+ "title",
+ ) as HTMLHeadingElement;
+ const progress: HTMLDivElement = document.getElementById(
+ "progress",
+ ) as HTMLDivElement;
const anubisVersion = j("anubis_version");
const basePrefix = j("anubis_base_prefix");
@@ -130,12 +145,12 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
progress.style.display = "none";
};
- status.innerHTML = t('calculating');
+ status.innerHTML = t("calculating");
for (const { value, name, msg } of dependencies) {
if (!value) {
ohNoes({
- titleMsg: `${t('missing_feature')} ${name}`,
+ titleMsg: `${t("missing_feature")} ${name}`,
statusMsg: msg,
imageSrc: imageURL("reject", anubisVersion, basePrefix),
});
@@ -148,20 +163,20 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
const process = algorithms[rules.algorithm];
if (!process) {
ohNoes({
- titleMsg: t('challenge_error'),
- statusMsg: t('challenge_error_msg'),
+ titleMsg: t("challenge_error"),
+ statusMsg: t("challenge_error_msg"),
imageSrc: imageURL("reject", anubisVersion, basePrefix),
});
return;
}
- status.innerHTML = `${t('calculating_difficulty')} ${rules.difficulty}, `;
+ status.innerHTML = `${t("calculating_difficulty")} ${rules.difficulty}, `;
progress.style.display = "inline-block";
// the whole text, including "Speed:", as a single node, because some browsers
// (Firefox mobile) present screen readers with each node as a separate piece
// of text.
- const rateText = document.createTextNode(`${t('speed')} 0kH/s`);
+ const rateText = document.createTextNode(`${t("speed")} 0kH/s`);
status.appendChild(rateText);
let lastSpeedUpdate = 0;
@@ -180,7 +195,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
// only update the speed every second so it's less visually distracting
if (delta - lastSpeedUpdate > 1000) {
lastSpeedUpdate = delta;
- rateText.data = `${t('speed')} ${(iters / delta).toFixed(3)}kH/s`;
+ rateText.data = `${t("speed")} ${(iters / delta).toFixed(3)}kH/s`;
}
// the probability of still being on the page is (1 - likelihood) ^ iters.
// by definition, half of the time the progress bar only gets to half, so
@@ -192,13 +207,14 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
const distance = (1 - Math.pow(probability, 2)) * 100;
progress["aria-valuenow"] = distance;
if (progress.firstElementChild !== null) {
- (progress.firstElementChild as HTMLElement).style.width = `${distance}%`;
+ (progress.firstElementChild as HTMLElement).style.width =
+ `${distance}%`;
}
if (probability < 0.1 && !showingApology) {
status.append(
document.createElement("br"),
- document.createTextNode(t('verification_longer')),
+ document.createTextNode(t("verification_longer")),
);
showingApology = true;
}
@@ -208,7 +224,9 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
console.log({ hash, nonce });
if (userReadDetails) {
- const container: HTMLDivElement = document.getElementById("progress") as HTMLDivElement;
+ const container: HTMLDivElement = document.getElementById(
+ "progress",
+ ) as HTMLDivElement;
// Style progress bar as a continue button
container.style.display = "flex";
@@ -224,7 +242,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
container.style.outlineOffset = "2px";
container.style.width = "min(20rem, 90%)";
container.style.margin = "1rem auto 2rem";
- container.innerHTML = t('finished_reading');
+ container.innerHTML = t("finished_reading");
function onDetailsExpand() {
const redir = getRedirectUrl();
@@ -255,8 +273,8 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
}
} catch (err) {
ohNoes({
- titleMsg: t('calculation_error'),
- statusMsg: `${t('calculation_error_msg')} ${err.message}`,
+ titleMsg: t("calculation_error"),
+ statusMsg: `${t("calculation_error_msg")} ${err.message}`,
imageSrc: imageURL("reject", anubisVersion, basePrefix),
});
}
diff --git a/web/js/worker/sha256-purejs.ts b/web/js/worker/sha256-purejs.ts
index 69060615..52ee1cda 100644
--- a/web/js/worker/sha256-purejs.ts
+++ b/web/js/worker/sha256-purejs.ts
@@ -1,4 +1,4 @@
-import { Sha256 } from '@aws-crypto/sha256-js';
+import { Sha256 } from "@aws-crypto/sha256-js";
const calculateSHA256 = (text) => {
const hash = new Sha256();
@@ -12,7 +12,7 @@ function toHexString(arr: Uint8Array): string {
.join("");
}
-addEventListener('message', async ({ data: eventData }) => {
+addEventListener("message", async ({ data: eventData }) => {
const { data, difficulty, threads } = eventData;
let nonce = eventData.nonce;
const isMainThread = nonce === 0;
@@ -21,7 +21,7 @@ addEventListener('message', async ({ data: eventData }) => {
const requiredZeroBytes = Math.floor(difficulty / 2);
const isDifficultyOdd = difficulty % 2 !== 0;
- for (; ;) {
+ for (;;) {
const hashBuffer = await calculateSHA256(data + nonce);
const hashArray = new Uint8Array(hashBuffer);
@@ -34,7 +34,7 @@ addEventListener('message', async ({ data: eventData }) => {
}
if (isValid && isDifficultyOdd) {
- if ((hashArray[requiredZeroBytes] >> 4) !== 0) {
+ if (hashArray[requiredZeroBytes] >> 4 !== 0) {
isValid = false;
}
}
@@ -55,7 +55,7 @@ addEventListener('message', async ({ data: eventData }) => {
/* Truncate the decimal portion of the nonce. This is a bit of an evil bit
* hack, but it works reliably enough. The core of why this works is:
- *
+ *
* > 13.4 % 1 !== 0
* true
* > 13 % 1 !== 0
@@ -70,4 +70,4 @@ addEventListener('message', async ({ data: eventData }) => {
postMessage(nonce);
}
}
-});
\ No newline at end of file
+});
diff --git a/web/js/worker/sha256-webcrypto.ts b/web/js/worker/sha256-webcrypto.ts
index c83f4665..d3cdeca2 100644
--- a/web/js/worker/sha256-webcrypto.ts
+++ b/web/js/worker/sha256-webcrypto.ts
@@ -6,7 +6,10 @@ const calculateSHA256 = async (input: string) => {
};
const toHexString = (byteArray: Uint8Array) => {
- return byteArray.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
+ return byteArray.reduce(
+ (str, byte) => str + byte.toString(16).padStart(2, "0"),
+ "",
+ );
};
addEventListener("message", async ({ data: eventData }) => {
@@ -18,7 +21,7 @@ addEventListener("message", async ({ data: eventData }) => {
const requiredZeroBytes = Math.floor(difficulty / 2);
const isDifficultyOdd = difficulty % 2 !== 0;
- for (; ;) {
+ for (;;) {
const hashBuffer = await calculateSHA256(data + nonce);
const hashArray = new Uint8Array(hashBuffer);
@@ -31,7 +34,7 @@ addEventListener("message", async ({ data: eventData }) => {
}
if (isValid && isDifficultyOdd) {
- if ((hashArray[requiredZeroBytes] >> 4) !== 0) {
+ if (hashArray[requiredZeroBytes] >> 4 !== 0) {
isValid = false;
}
}
@@ -52,7 +55,7 @@ addEventListener("message", async ({ data: eventData }) => {
/* Truncate the decimal portion of the nonce. This is a bit of an evil bit
* hack, but it works reliably enough. The core of why this works is:
- *
+ *
* > 13.4 % 1 !== 0
* true
* > 13 % 1 !== 0
@@ -67,4 +70,4 @@ addEventListener("message", async ({ data: eventData }) => {
postMessage(nonce);
}
}
-});
\ No newline at end of file
+});
diff --git a/xess/postcss.config.js b/xess/postcss.config.js
index 7084661d..13c708c6 100644
--- a/xess/postcss.config.js
+++ b/xess/postcss.config.js
@@ -5,4 +5,4 @@ module.exports = {
}),
require("postcss-url")({ url: "inline" }),
],
-};
\ No newline at end of file
+};
diff --git a/yeetfile.js b/yeetfile.js
index 47749aff..caab0a5e 100644
--- a/yeetfile.js
+++ b/yeetfile.js
@@ -1,43 +1,41 @@
$`npm run assets`;
-[
- "amd64",
- "arm64",
- "ppc64le",
- "riscv64",
-].forEach(goarch => {
- [deb, rpm, tarball].forEach(method => method.build({
- name: "anubis",
- description: "Anubis weighs the souls of incoming HTTP requests and uses a sha256 proof-of-work challenge in order to protect upstream resources from scraper bots.",
- homepage: "https://anubis.techaro.lol",
- license: "MIT",
- goarch,
+["amd64", "arm64", "ppc64le", "riscv64"].forEach((goarch) => {
+ [deb, rpm, tarball].forEach((method) =>
+ method.build({
+ name: "anubis",
+ description:
+ "Anubis weighs the souls of incoming HTTP requests and uses a sha256 proof-of-work challenge in order to protect upstream resources from scraper bots.",
+ homepage: "https://anubis.techaro.lol",
+ license: "MIT",
+ goarch,
- documentation: {
- "./README.md": "README.md",
- "./LICENSE": "LICENSE",
- "./data/botPolicies.yaml": "botPolicies.yaml",
- },
+ documentation: {
+ "./README.md": "README.md",
+ "./LICENSE": "LICENSE",
+ "./data/botPolicies.yaml": "botPolicies.yaml",
+ },
- build: ({ bin, etc, systemd, doc }) => {
- $`go build -o ${bin}/anubis -ldflags '-s -w -extldflags "-static" -X "github.com/TecharoHQ/anubis.Version=${git.tag()}"' ./cmd/anubis`;
- $`go build -o ${bin}/anubis-robots2policy -ldflags '-s -w -extldflags "-static" -X "github.com/TecharoHQ/anubis.Version=${git.tag()}"' ./cmd/robots2policy`;
+ build: ({ bin, etc, systemd, doc }) => {
+ $`go build -o ${bin}/anubis -ldflags '-s -w -extldflags "-static" -X "github.com/TecharoHQ/anubis.Version=${git.tag()}"' ./cmd/anubis`;
+ $`go build -o ${bin}/anubis-robots2policy -ldflags '-s -w -extldflags "-static" -X "github.com/TecharoHQ/anubis.Version=${git.tag()}"' ./cmd/robots2policy`;
- file.install("./run/anubis@.service", `${systemd}/anubis@.service`);
- file.install("./run/default.env", `${etc}/default.env`);
+ file.install("./run/anubis@.service", `${systemd}/anubis@.service`);
+ file.install("./run/default.env", `${etc}/default.env`);
- $`mkdir -p ${doc}/docs`
- $`cp -a docs/docs ${doc}`;
- $`find ${doc} -name _category_.json -delete`;
- $`mkdir -p ${doc}/data`;
- $`cp -a data/apps ${doc}/data/apps`;
- $`cp -a data/bots ${doc}/data/bots`;
- $`cp -a data/clients ${doc}/data/clients`;
- $`cp -a data/common ${doc}/data/common`;
- $`cp -a data/crawlers ${doc}/data/crawlers`;
- $`cp -a data/meta ${doc}/data/meta`;
- },
- }));
+ $`mkdir -p ${doc}/docs`;
+ $`cp -a docs/docs ${doc}`;
+ $`find ${doc} -name _category_.json -delete`;
+ $`mkdir -p ${doc}/data`;
+ $`cp -a data/apps ${doc}/data/apps`;
+ $`cp -a data/bots ${doc}/data/bots`;
+ $`cp -a data/clients ${doc}/data/clients`;
+ $`cp -a data/common ${doc}/data/common`;
+ $`cp -a data/crawlers ${doc}/data/crawlers`;
+ $`cp -a data/meta ${doc}/data/meta`;
+ },
+ }),
+ );
});
// NOTE(Xe): Fixes #217. This is a "half baked" tarball that includes the harder
@@ -46,41 +44,41 @@ $`npm run assets`;
// model into the bazaar of round holes that various modern languages use. Needless
// to say, this makes adoption easier.
tarball.build({
- name: "anubis-src-vendor",
- license: "MIT",
- // XXX(Xe): This is needed otherwise go will be very sad.
- platform: yeet.goos,
- goarch: yeet.goarch,
+ name: "anubis-src-vendor",
+ license: "MIT",
+ // XXX(Xe): This is needed otherwise go will be very sad.
+ platform: yeet.goos,
+ goarch: yeet.goarch,
- build: ({ out }) => {
- // prepare clean checkout in $out
- $`git archive --format=tar HEAD | tar xC ${out}`;
- // vendor Go dependencies
- $`cd ${out} && go mod vendor`;
- // write VERSION file
- $`echo ${git.tag()} > ${out}/VERSION`;
- },
+ build: ({ out }) => {
+ // prepare clean checkout in $out
+ $`git archive --format=tar HEAD | tar xC ${out}`;
+ // vendor Go dependencies
+ $`cd ${out} && go mod vendor`;
+ // write VERSION file
+ $`echo ${git.tag()} > ${out}/VERSION`;
+ },
- mkFilename: ({ name, version }) => `${name}-${version}`,
+ mkFilename: ({ name, version }) => `${name}-${version}`,
});
tarball.build({
- name: "anubis-src-vendor-npm",
- license: "MIT",
- // XXX(Xe): This is needed otherwise go will be very sad.
- platform: yeet.goos,
- goarch: yeet.goarch,
+ name: "anubis-src-vendor-npm",
+ license: "MIT",
+ // XXX(Xe): This is needed otherwise go will be very sad.
+ platform: yeet.goos,
+ goarch: yeet.goarch,
- build: ({ out }) => {
- // prepare clean checkout in $out
- $`git archive --format=tar HEAD | tar xC ${out}`;
- // vendor Go dependencies
- $`cd ${out} && go mod vendor`;
- // build NPM-bound dependencies
- $`cd ${out} && npm ci && npm run assets && rm -rf node_modules`
- // write VERSION file
- $`echo ${git.tag()} > ${out}/VERSION`;
- },
+ build: ({ out }) => {
+ // prepare clean checkout in $out
+ $`git archive --format=tar HEAD | tar xC ${out}`;
+ // vendor Go dependencies
+ $`cd ${out} && go mod vendor`;
+ // build NPM-bound dependencies
+ $`cd ${out} && npm ci && npm run assets && rm -rf node_modules`;
+ // write VERSION file
+ $`echo ${git.tag()} > ${out}/VERSION`;
+ },
- mkFilename: ({ name, version }) => `${name}-${version}`,
-});
\ No newline at end of file
+ mkFilename: ({ name, version }) => `${name}-${version}`,
+});