mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-09 18:18:49 +00:00
Compare commits
10 Commits
Xe/nerf-tr
...
Xe/default
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf44607f8f | ||
|
|
a735770c93 | ||
|
|
de796325bc | ||
|
|
bf42014ac3 | ||
|
|
0ef3461816 | ||
|
|
7d7028d25c | ||
|
|
9affd2edf4 | ||
|
|
26b6d8a91a | ||
|
|
958992a69a | ||
|
|
221d9f2072 |
15
.github/workflows/smoke-tests.yml
vendored
15
.github/workflows/smoke-tests.yml
vendored
@@ -18,7 +18,9 @@ jobs:
|
|||||||
- git-push
|
- git-push
|
||||||
- healthcheck
|
- healthcheck
|
||||||
- i18n
|
- i18n
|
||||||
runs-on: ubuntu-24.04
|
- palemoon/amd64
|
||||||
|
#- palemoon/i386
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@@ -43,3 +45,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd test/${{ matrix.test }}
|
cd test/${{ matrix.test }}
|
||||||
backoff-retry --try-count 10 ./test.sh
|
backoff-retry --try-count 10 ./test.sh
|
||||||
|
|
||||||
|
- name: Sanitize artifact name
|
||||||
|
if: always()
|
||||||
|
run: echo "ARTIFACT_NAME=${{ matrix.test }}" | sed 's|/|-|g' >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: ${{ env.ARTIFACT_NAME }}
|
||||||
|
path: test/${{ matrix.test }}/var
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ import (
|
|||||||
"github.com/TecharoHQ/anubis"
|
"github.com/TecharoHQ/anubis"
|
||||||
"github.com/TecharoHQ/anubis/data"
|
"github.com/TecharoHQ/anubis/data"
|
||||||
"github.com/TecharoHQ/anubis/internal"
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
|
||||||
libanubis "github.com/TecharoHQ/anubis/lib"
|
libanubis "github.com/TecharoHQ/anubis/lib"
|
||||||
botPolicy "github.com/TecharoHQ/anubis/lib/policy"
|
botPolicy "github.com/TecharoHQ/anubis/lib/policy"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
"github.com/TecharoHQ/anubis/web"
|
"github.com/TecharoHQ/anubis/web"
|
||||||
"github.com/facebookgo/flagenv"
|
"github.com/facebookgo/flagenv"
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
- import: (data)/bots/cloudflare-workers.yaml
|
- import: (data)/bots/cloudflare-workers.yaml
|
||||||
- import: (data)/bots/headless-browsers.yaml
|
- import: (data)/bots/headless-browsers.yaml
|
||||||
- import: (data)/bots/us-ai-scraper.yaml
|
- import: (data)/bots/us-ai-scraper.yaml
|
||||||
|
- import: (data)/bots/custom-async-http-client.yaml
|
||||||
|
|||||||
5
data/bots/custom-async-http-client.yaml
Normal file
5
data/bots/custom-async-http-client.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
- name: "custom-async-http-client"
|
||||||
|
user_agent_regex: "Custom-AsyncHttpClient"
|
||||||
|
action: WEIGH
|
||||||
|
weight:
|
||||||
|
adjust: 10
|
||||||
@@ -13,18 +13,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
<!-- This changes the project to: -->
|
<!-- This changes the project to: -->
|
||||||
|
|
||||||
## v1.21.2: Minfilia Warde - Echo 2
|
- The [Thoth client](https://anubis.techaro.lol/docs/admin/thoth) is now public in the repo instead of being an internal package.
|
||||||
|
- [Custom-AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client)'s default User-Agent has an increased weight by default ([#852](https://github.com/TecharoHQ/anubis/issues/852)).
|
||||||
|
- The [`segments`](./admin/configuration/expressions.mdx#segments) function was added for splitting a path into its slash-separated segments.
|
||||||
|
|
||||||
|
## v1.21.3: Minfilia Warde - Echo 3
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
#### Fixes a problem with nonstandard URLs and redirects
|
#### Fixes a problem with nonstandard URLs and redirects
|
||||||
|
|
||||||
|
Fixes [GHSA-jhjj-2g64-px7c](https://github.com/TecharoHQ/anubis/security/advisories/GHSA-jhjj-2g64-px7c).
|
||||||
|
|
||||||
This could allow an attacker to craft an Anubis pass-challenge URL that forces a redirect to nonstandard URLs, such as the `javascript:` scheme which executes arbitrary JavaScript code in a browser context when the user clicks the "Try again" button.
|
This could allow an attacker to craft an Anubis pass-challenge URL that forces a redirect to nonstandard URLs, such as the `javascript:` scheme which executes arbitrary JavaScript code in a browser context when the user clicks the "Try again" button.
|
||||||
|
|
||||||
This has been fixed by disallowing any URLs without the scheme `http` or `https`.
|
This has been fixed by disallowing any URLs without the scheme `http` or `https`.
|
||||||
|
|
||||||
Additionally, the "Try again" button has been fixed to completely ignore the user-supplied redirect location. It now redirects to the home page (`/`).
|
Additionally, the "Try again" button has been fixed to completely ignore the user-supplied redirect location. It now redirects to the home page (`/`).
|
||||||
|
|
||||||
|
## v1.21.2: Minfilia Warde - Echo 2
|
||||||
|
|
||||||
|
This contained an incomplete fix for [GHSA-jhjj-2g64-px7c](https://github.com/TecharoHQ/anubis/security/advisories/GHSA-jhjj-2g64-px7c). Do not use this version.
|
||||||
|
|
||||||
## v1.21.1: Minfilia Warde - Echo 1
|
## v1.21.1: Minfilia Warde - Echo 1
|
||||||
|
|
||||||
- Expired records are now properly removed from bbolt databases ([#848](https://github.com/TecharoHQ/anubis/pull/848)).
|
- Expired records are now properly removed from bbolt databases ([#848](https://github.com/TecharoHQ/anubis/pull/848)).
|
||||||
|
|||||||
@@ -232,6 +232,39 @@ This is best applied when doing explicit block rules, eg:
|
|||||||
|
|
||||||
It seems counter-intuitive to allow known bad clients through sometimes, but this allows you to confuse attackers by making Anubis' behavior random. Adjust the thresholds and numbers as facts and circumstances demand.
|
It seems counter-intuitive to allow known bad clients through sometimes, but this allows you to confuse attackers by making Anubis' behavior random. Adjust the thresholds and numbers as facts and circumstances demand.
|
||||||
|
|
||||||
|
### `segments`
|
||||||
|
|
||||||
|
Available in `bot` expressions.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function segments(path: string): string[];
|
||||||
|
```
|
||||||
|
|
||||||
|
`segments` returns the number of slash-separated path segments, ignoring the leading slash. Here is what it will return with some common paths:
|
||||||
|
|
||||||
|
| Input | Output |
|
||||||
|
| :----------------------- | :--------------------- |
|
||||||
|
| `segments("/")` | `[""]` |
|
||||||
|
| `segments("/foo/bar")` | `["foo", "bar"] ` |
|
||||||
|
| `segments("/users/xe/")` | `["users", "xe", ""] ` |
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
If the path ends with a `/`, then the last element of the result will be an empty string. This is because `/users/xe` and `/users/xe/` are semantically different paths.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
This is useful if you want to write rules that allow requests that have no query parameters only if they have less than two path segments:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: two-path-segments-no-query
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- size(query) == 0
|
||||||
|
- size(segments(path)) < 2
|
||||||
|
```
|
||||||
|
|
||||||
## Life advice
|
## Life advice
|
||||||
|
|
||||||
Expressions are very powerful. This is a benefit and a burden. If you are not careful with your expression targeting, you will be liable to get yourself into trouble. If you are at all in doubt, throw a `CHALLENGE` over a `DENY`. Legitimate users can easily work around a `CHALLENGE` result with a [proof of work challenge](../../design/why-proof-of-work.mdx). Bots are less likely to be able to do this.
|
Expressions are very powerful. This is a benefit and a burden. If you are not careful with your expression targeting, you will be liable to get yourself into trouble. If you are at all in doubt, throw a `CHALLENGE` over a `DENY`. Legitimate users can easily work around a `CHALLENGE` result with a [proof of work challenge](../../design/why-proof-of-work.mdx). Bots are less likely to be able to do this.
|
||||||
|
|||||||
20
docs/package-lock.json
generated
20
docs/package-lock.json
generated
@@ -5908,9 +5908,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
@@ -6496,16 +6496,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/compression": {
|
"node_modules/compression": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||||
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
|
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"compressible": "~2.0.18",
|
"compressible": "~2.0.18",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"negotiator": "~0.6.4",
|
"negotiator": "~0.6.4",
|
||||||
"on-headers": "~1.0.2",
|
"on-headers": "~1.1.0",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
},
|
},
|
||||||
@@ -13562,9 +13562,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/on-headers": {
|
"node_modules/on-headers": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import (
|
|||||||
"github.com/TecharoHQ/anubis"
|
"github.com/TecharoHQ/anubis"
|
||||||
"github.com/TecharoHQ/anubis/data"
|
"github.com/TecharoHQ/anubis/data"
|
||||||
"github.com/TecharoHQ/anubis/internal"
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth/thothmock"
|
|
||||||
"github.com/TecharoHQ/anubis/lib/policy"
|
"github.com/TecharoHQ/anubis/lib/policy"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth/thothmock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -926,3 +926,42 @@ func TestPassChallengeXSS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestXForwardedForNoDoubleComma(t *testing.T) {
|
||||||
|
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
|
||||||
|
fmt.Fprintln(w, "OK")
|
||||||
|
})
|
||||||
|
|
||||||
|
h = internal.XForwardedForToXRealIP(h)
|
||||||
|
h = internal.XForwardedForUpdate(false, h)
|
||||||
|
|
||||||
|
pol := loadPolicies(t, "testdata/permissive.yaml", 4)
|
||||||
|
|
||||||
|
srv := spawnAnubis(t, Options{
|
||||||
|
Next: h,
|
||||||
|
Policy: pol,
|
||||||
|
})
|
||||||
|
ts := httptest.NewServer(srv)
|
||||||
|
t.Cleanup(ts.Close)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("X-Real-Ip", "10.0.0.1")
|
||||||
|
|
||||||
|
resp, err := ts.Client().Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("response status is wrong, wanted %d but got: %s", http.StatusOK, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if xff := resp.Header.Get("X-Forwarded-For"); strings.HasPrefix(xff, ",,") {
|
||||||
|
t.Errorf("X-Forwarded-For has two leading commas: %q", xff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis"
|
"github.com/TecharoHQ/anubis"
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth/thothmock"
|
|
||||||
"github.com/TecharoHQ/anubis/lib/policy"
|
"github.com/TecharoHQ/anubis/lib/policy"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth/thothmock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInvalidChallengeMethod(t *testing.T) {
|
func TestInvalidChallengeMethod(t *testing.T) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package expressions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"github.com/google/cel-go/common/types"
|
"github.com/google/cel-go/common/types"
|
||||||
@@ -54,6 +55,28 @@ func BotEnvironment() (*cel.Env, error) {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
cel.Function("segments",
|
||||||
|
cel.Overload("segments_string_list_string",
|
||||||
|
[]*cel.Type{cel.StringType},
|
||||||
|
cel.ListType(cel.StringType),
|
||||||
|
cel.UnaryBinding(func(path ref.Val) ref.Val {
|
||||||
|
pathStrType, ok := path.(types.String)
|
||||||
|
if !ok {
|
||||||
|
return types.ValOrErr(path, "path is not a string, but is %T", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pathStr := string(pathStrType)
|
||||||
|
if !strings.HasPrefix(pathStr, "/") {
|
||||||
|
return types.ValOrErr(path, "path does not start with /")
|
||||||
|
}
|
||||||
|
|
||||||
|
pathList := strings.Split(string(pathStr), "/")[1:]
|
||||||
|
|
||||||
|
return types.NewStringList(types.DefaultTypeAdapter, pathList)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,99 +12,228 @@ func TestBotEnvironment(t *testing.T) {
|
|||||||
t.Fatalf("failed to create bot environment: %v", err)
|
t.Fatalf("failed to create bot environment: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
t.Run("missingHeader", func(t *testing.T) {
|
||||||
name string
|
tests := []struct {
|
||||||
expression string
|
name string
|
||||||
headers map[string]string
|
expression string
|
||||||
expected types.Bool
|
headers map[string]string
|
||||||
description string
|
expected types.Bool
|
||||||
}{
|
description string
|
||||||
{
|
}{
|
||||||
name: "missing-header",
|
{
|
||||||
expression: `missingHeader(headers, "Missing-Header")`,
|
name: "missing-header",
|
||||||
headers: map[string]string{
|
expression: `missingHeader(headers, "Missing-Header")`,
|
||||||
"User-Agent": "test-agent",
|
headers: map[string]string{
|
||||||
"Content-Type": "application/json",
|
"User-Agent": "test-agent",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
expected: types.Bool(true),
|
||||||
|
description: "should return true when header is missing",
|
||||||
},
|
},
|
||||||
expected: types.Bool(true),
|
{
|
||||||
description: "should return true when header is missing",
|
name: "existing-header",
|
||||||
},
|
expression: `missingHeader(headers, "User-Agent")`,
|
||||||
{
|
headers: map[string]string{
|
||||||
name: "existing-header",
|
"User-Agent": "test-agent",
|
||||||
expression: `missingHeader(headers, "User-Agent")`,
|
"Content-Type": "application/json",
|
||||||
headers: map[string]string{
|
},
|
||||||
"User-Agent": "test-agent",
|
expected: types.Bool(false),
|
||||||
"Content-Type": "application/json",
|
description: "should return false when header exists",
|
||||||
},
|
},
|
||||||
expected: types.Bool(false),
|
{
|
||||||
description: "should return false when header exists",
|
name: "case-sensitive",
|
||||||
},
|
expression: `missingHeader(headers, "user-agent")`,
|
||||||
{
|
headers: map[string]string{
|
||||||
name: "case-sensitive",
|
"User-Agent": "test-agent",
|
||||||
expression: `missingHeader(headers, "user-agent")`,
|
},
|
||||||
headers: map[string]string{
|
expected: types.Bool(true),
|
||||||
"User-Agent": "test-agent",
|
description: "should be case-sensitive (user-agent != User-Agent)",
|
||||||
},
|
},
|
||||||
expected: types.Bool(true),
|
{
|
||||||
description: "should be case-sensitive (user-agent != User-Agent)",
|
name: "empty-headers",
|
||||||
},
|
expression: `missingHeader(headers, "Any-Header")`,
|
||||||
{
|
headers: map[string]string{},
|
||||||
name: "empty-headers",
|
expected: types.Bool(true),
|
||||||
expression: `missingHeader(headers, "Any-Header")`,
|
description: "should return true for any header when map is empty",
|
||||||
headers: map[string]string{},
|
|
||||||
expected: types.Bool(true),
|
|
||||||
description: "should return true for any header when map is empty",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "real-world-sec-ch-ua",
|
|
||||||
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
|
||||||
headers: map[string]string{
|
|
||||||
"User-Agent": "curl/7.68.0",
|
|
||||||
"Accept": "*/*",
|
|
||||||
"Host": "example.com",
|
|
||||||
},
|
},
|
||||||
expected: types.Bool(true),
|
{
|
||||||
description: "should detect missing browser-specific headers from bots",
|
name: "real-world-sec-ch-ua",
|
||||||
},
|
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
||||||
{
|
headers: map[string]string{
|
||||||
name: "browser-with-sec-ch-ua",
|
"User-Agent": "curl/7.68.0",
|
||||||
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
"Accept": "*/*",
|
||||||
headers: map[string]string{
|
"Host": "example.com",
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
},
|
||||||
"Sec-Ch-Ua": `"Chrome"; v="91", "Not A Brand"; v="99"`,
|
expected: types.Bool(true),
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
description: "should detect missing browser-specific headers from bots",
|
||||||
},
|
},
|
||||||
expected: types.Bool(false),
|
{
|
||||||
description: "should return false when browser sends Sec-Ch-Ua header",
|
name: "browser-with-sec-ch-ua",
|
||||||
},
|
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
||||||
}
|
headers: map[string]string{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
|
"Sec-Ch-Ua": `"Chrome"; v="91", "Not A Brand"; v="99"`,
|
||||||
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||||
|
},
|
||||||
|
expected: types.Bool(false),
|
||||||
|
description: "should return false when browser sends Sec-Ch-Ua header",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
prog, err := Compile(env, tt.expression)
|
prog, err := Compile(env, tt.expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, _, err := prog.Eval(map[string]interface{}{
|
result, _, err := prog.Eval(map[string]interface{}{
|
||||||
"headers": tt.headers,
|
"headers": tt.headers,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
}
|
||||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result != tt.expected {
|
t.Run("function-compilation", func(t *testing.T) {
|
||||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
src := `missingHeader(headers, "Test-Header")`
|
||||||
|
_, err := Compile(env, src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to compile missingHeader expression: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
|
||||||
t.Run("function-compilation", func(t *testing.T) {
|
t.Run("segments", func(t *testing.T) {
|
||||||
src := `missingHeader(headers, "Test-Header")`
|
for _, tt := range []struct {
|
||||||
_, err := Compile(env, src)
|
name string
|
||||||
if err != nil {
|
description string
|
||||||
t.Fatalf("failed to compile missingHeader expression: %v", err)
|
expression string
|
||||||
|
path string
|
||||||
|
expected types.Bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
description: "/ should have one path segment",
|
||||||
|
expression: `size(segments(path)) == 1`,
|
||||||
|
path: "/",
|
||||||
|
expected: types.Bool(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two segments without trailing slash",
|
||||||
|
description: "/user/foo should have two segments",
|
||||||
|
expression: `size(segments(path)) == 2`,
|
||||||
|
path: "/user/foo",
|
||||||
|
expected: types.Bool(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "at least two segments",
|
||||||
|
description: "/foo/bar/ should have at least two path segments",
|
||||||
|
expression: `size(segments(path)) >= 2`,
|
||||||
|
path: "/foo/bar/",
|
||||||
|
expected: types.Bool(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "at most two segments",
|
||||||
|
description: "/foo/bar/ does not have less than two path segments",
|
||||||
|
expression: `size(segments(path)) < 2`,
|
||||||
|
path: "/foo/bar/",
|
||||||
|
expected: types.Bool(false),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
prog, err := Compile(env, tt.expression)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, _, err := prog.Eval(map[string]interface{}{
|
||||||
|
"path": tt.path,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
expression string
|
||||||
|
env any
|
||||||
|
wantFailCompile bool
|
||||||
|
wantFailEval bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "segments of headers",
|
||||||
|
description: "headers are not a path list",
|
||||||
|
expression: `segments(headers)`,
|
||||||
|
env: map[string]any{
|
||||||
|
"headers": map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFailCompile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid path type",
|
||||||
|
description: "a path should be a sting",
|
||||||
|
expression: `size(segments(path)) != 0`,
|
||||||
|
env: map[string]any{
|
||||||
|
"path": 4,
|
||||||
|
},
|
||||||
|
wantFailEval: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid path",
|
||||||
|
description: "a path should start with a leading slash",
|
||||||
|
expression: `size(segments(path)) != 0`,
|
||||||
|
env: map[string]any{
|
||||||
|
"path": "foo",
|
||||||
|
},
|
||||||
|
wantFailEval: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
prog, err := Compile(env, tt.expression)
|
||||||
|
if err != nil {
|
||||||
|
if !tt.wantFailCompile {
|
||||||
|
t.Log(tt.description)
|
||||||
|
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = prog.Eval(tt.env)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Log(tt.description)
|
||||||
|
t.Fatal("wanted an error but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("function-compilation", func(t *testing.T) {
|
||||||
|
src := `size(segments(path)) <= 2`
|
||||||
|
_, err := Compile(env, src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to compile missingHeader expression: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
"github.com/TecharoHQ/anubis/lib/store"
|
"github.com/TecharoHQ/anubis/lib/store"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/TecharoHQ/anubis"
|
"github.com/TecharoHQ/anubis"
|
||||||
"github.com/TecharoHQ/anubis/data"
|
"github.com/TecharoHQ/anubis/data"
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth/thothmock"
|
"github.com/TecharoHQ/anubis/lib/thoth/thothmock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefaultPolicyMustParse(t *testing.T) {
|
func TestDefaultPolicyMustParse(t *testing.T) {
|
||||||
|
|||||||
4
lib/testdata/permissive.yaml
vendored
Normal file
4
lib/testdata/permissive.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
bots:
|
||||||
|
- import: (data)/common/allow-private-addresses.yaml
|
||||||
|
|
||||||
|
dnsbl: false
|
||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
iptoasnv1 "github.com/TecharoHQ/thoth-proto/gen/techaro/thoth/iptoasn/v1"
|
iptoasnv1 "github.com/TecharoHQ/thoth-proto/gen/techaro/thoth/iptoasn/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
|
||||||
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ checker.Impl = &thoth.GeoIPChecker{}
|
var _ checker.Impl = &thoth.GeoIPChecker{}
|
||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth/thothmock"
|
"github.com/TecharoHQ/anubis/lib/thoth/thothmock"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/internal/thoth"
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WithMockThoth(t *testing.T) context.Context {
|
func WithMockThoth(t *testing.T) context.Context {
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@techaro/anubis",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.21.2",
|
"version": "1.21.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@techaro/anubis",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.21.2",
|
"version": "1.21.3",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cssnano": "^7.1.0",
|
"cssnano": "^7.1.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@techaro/anubis",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.21.2",
|
"version": "1.21.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
33
test/cmd/cipra/internal/containerip.go
Normal file
33
test/cmd/cipra/internal/containerip.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetContainerIPAddress returns the first non-empty IP address of the container with the given name.
|
||||||
|
// It returns the IP address as a string or an error.
|
||||||
|
func GetContainerIPAddress(containerName string) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get container details
|
||||||
|
containerJSON, err := cli.ContainerInspect(ctx, containerName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all networks and return the first IP address found
|
||||||
|
for _, net := range containerJSON.NetworkSettings.Networks {
|
||||||
|
if net.IPAddress != "" {
|
||||||
|
return net.IPAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("no IP address found for container %q", containerName)
|
||||||
|
}
|
||||||
50
test/cmd/cipra/internal/getlanip.go
Normal file
50
test/cmd/cipra/internal/getlanip.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLANIP returns the first non-loopback IPv4 LAN IP address.
|
||||||
|
func GetLANIP() (net.IP, error) {
|
||||||
|
ifaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
// Skip down or loopback interfaces
|
||||||
|
if iface.Flags&(net.FlagUp|net.FlagLoopback) != net.FlagUp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue // skip interfaces we can't query
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = ip.To4()
|
||||||
|
if ip == nil {
|
||||||
|
continue // not an IPv4 address
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no connected LAN IPv4 address found")
|
||||||
|
}
|
||||||
34
test/cmd/cipra/internal/unbreakdocker.go
Normal file
34
test/cmd/cipra/internal/unbreakdocker.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnbreakDocker connects the container named after the current hostname
|
||||||
|
// to the specified Docker network.
|
||||||
|
func UnbreakDocker(networkName string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cli.NetworkConnect(ctx, networkName, hostname, &network.EndpointSettings{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Connected container %q to network %q\n", hostname, networkName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
114
test/cmd/cipra/main.go
Normal file
114
test/cmd/cipra/main.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/TecharoHQ/anubis/test/cmd/cipra/internal"
|
||||||
|
"github.com/facebookgo/flagenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bind = flag.String("bind", ":9090", "TCP host:port to bind HTTP on")
|
||||||
|
browserBin = flag.String("browser-bin", "palemoon", "browser binary name")
|
||||||
|
browserContainerName = flag.String("browser-container-name", "palemoon", "browser container name")
|
||||||
|
composeName = flag.String("compose-name", "", "docker compose base name for resources")
|
||||||
|
vncServerContainer = flag.String("vnc-container-name", "display", "VNC host:port (NOT a display number)")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flagenv.Parse()
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
lanip, err := internal.GetLANIP()
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("TARGET", fmt.Sprintf("%s%s", lanip.String(), *bind))
|
||||||
|
|
||||||
|
http.HandleFunc("/{$}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "OK", http.StatusOK)
|
||||||
|
log.Println("got termination signal", r.RequestURI)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Handler: http.DefaultServeMux,
|
||||||
|
Addr: *bind,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := RunScript(ctx, "docker", "compose", "up", "-d"); err != nil {
|
||||||
|
log.Fatalf("can't start project: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer RunScript(ctx, "docker", "compose", "down", "-t", "1")
|
||||||
|
defer RunScript(ctx, "docker", "compose", "rm", "-f")
|
||||||
|
|
||||||
|
internal.UnbreakDocker(*composeName + "_default")
|
||||||
|
|
||||||
|
if err := RunScript(ctx, "docker", "exec", fmt.Sprintf("%s-%s-1", *composeName, *browserContainerName), "bash", "/hack/scripts/install-cert.sh"); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RunScript(ctx, "docker", "exec", fmt.Sprintf("%s-%s-1", *composeName, *browserContainerName), *browserBin, "https://relayd"); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Close()
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunScript(ctx context.Context, args ...string) error {
|
||||||
|
var err error
|
||||||
|
backoff := 250 * time.Millisecond
|
||||||
|
|
||||||
|
for attempt := 0; attempt < 5; attempt++ {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
log.Printf("Running command: %s", strings.Join(args, " "))
|
||||||
|
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
log.Printf("attempt=%d code=%d", attempt, exitErr.ExitCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Attempt %d failed: %v %T", attempt+1, err, err)
|
||||||
|
log.Printf("Retrying in %v...", backoff)
|
||||||
|
time.Sleep(backoff)
|
||||||
|
backoff *= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("script failed after 5 attempts: %w", err)
|
||||||
|
}
|
||||||
25
test/go.mod
25
test/go.mod
@@ -6,6 +6,7 @@ replace github.com/TecharoHQ/anubis => ..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/TecharoHQ/anubis v1.19.1
|
github.com/TecharoHQ/anubis v1.19.1
|
||||||
|
github.com/docker/docker v28.0.1+incompatible
|
||||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
)
|
)
|
||||||
@@ -13,27 +14,38 @@ require (
|
|||||||
require (
|
require (
|
||||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 // indirect
|
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 // indirect
|
||||||
cel.dev/expr v0.24.0 // indirect
|
cel.dev/expr v0.24.0 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/TecharoHQ/thoth-proto v0.4.0 // indirect
|
github.com/TecharoHQ/thoth-proto v0.4.0 // indirect
|
||||||
github.com/a-h/templ v0.3.906 // indirect
|
github.com/a-h/templ v0.3.906 // indirect
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/ebitengine/purego v0.8.4 // indirect
|
github.com/ebitengine/purego v0.8.4 // indirect
|
||||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
|
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
|
||||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
|
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/gaissmai/bart v0.20.5 // indirect
|
github.com/gaissmai/bart v0.20.5 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
github.com/google/cel-go v0.25.0 // indirect
|
github.com/google/cel-go v0.25.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/jsha/minica v1.1.0 // indirect
|
github.com/jsha/minica v1.1.0 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be // indirect
|
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be // indirect
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
@@ -45,15 +57,22 @@ require (
|
|||||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.etcd.io/bbolt v1.4.2 // indirect
|
go.etcd.io/bbolt v1.4.2 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||||
golang.org/x/net v0.42.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/sys v0.34.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.27.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||||
google.golang.org/grpc v1.73.0 // indirect
|
google.golang.org/grpc v1.73.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
|
gotest.tools/v3 v3.5.2 // indirect
|
||||||
k8s.io/apimachinery v0.33.2 // indirect
|
k8s.io/apimachinery v0.33.2 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||||
|
|||||||
53
test/go.sum
53
test/go.sum
@@ -24,6 +24,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
|||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
||||||
|
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
@@ -32,7 +34,6 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS
|
|||||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
@@ -61,6 +62,7 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
|||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
|
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
|
||||||
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
@@ -83,10 +85,14 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs
|
|||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/jsha/minica v1.1.0 h1:O2ZbzAN75w4RTB+5+HfjIEvY5nxRqDlwj3ZlLVG5JD8=
|
github.com/jsha/minica v1.1.0 h1:O2ZbzAN75w4RTB+5+HfjIEvY5nxRqDlwj3ZlLVG5JD8=
|
||||||
github.com/jsha/minica v1.1.0/go.mod h1:dxC3wNmD+gU1ewXo/R8jB2ihB6wNpyXrG8aUk5Iuf/k=
|
github.com/jsha/minica v1.1.0/go.mod h1:dxC3wNmD+gU1ewXo/R8jB2ihB6wNpyXrG8aUk5Iuf/k=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
@@ -164,6 +170,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
|
|||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
||||||
@@ -174,6 +182,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u
|
|||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||||
@@ -182,28 +194,57 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5J
|
|||||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
|
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
|
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
@@ -214,6 +255,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
|
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
|
|||||||
2
test/i18n/var/.gitignore
vendored
Normal file
2
test/i18n/var/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
51
test/lib/lib.sh
Normal file
51
test/lib/lib.sh
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||||
|
(cd $REPO_ROOT && go install ./utils/cmd/...)
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
pkill -P $$
|
||||||
|
|
||||||
|
if [ -f "docker-compose.yaml" ]; then
|
||||||
|
docker compose down -t 1 || :
|
||||||
|
docker compose rm -f || :
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT SIGINT
|
||||||
|
|
||||||
|
function build_anubis_ko() {
|
||||||
|
(
|
||||||
|
cd $REPO_ROOT && npm ci && npm run assets
|
||||||
|
)
|
||||||
|
(
|
||||||
|
cd $REPO_ROOT &&
|
||||||
|
VERSION=devel ko build \
|
||||||
|
--platform=all \
|
||||||
|
--base-import-paths \
|
||||||
|
--tags="latest" \
|
||||||
|
--image-user=1000 \
|
||||||
|
--image-annotation="" \
|
||||||
|
--image-label="" \
|
||||||
|
./cmd/anubis \
|
||||||
|
--local
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mint_cert() {
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: mint_cert <domain.name>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
domainName="$1"
|
||||||
|
|
||||||
|
# If the transient local TLS certificate doesn't exist, mint a new one
|
||||||
|
if [ ! -f "${REPO_ROOT}/test/pki/${domainName}/cert.pem" ]; then
|
||||||
|
# Subshell to contain the directory change
|
||||||
|
(
|
||||||
|
cd ${REPO_ROOT}/test/pki &&
|
||||||
|
mkdir -p "${domainName}" &&
|
||||||
|
go tool minica -domains "${domainName}" &&
|
||||||
|
cd "${domainName}" &&
|
||||||
|
chmod 666 *
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
}
|
||||||
5
test/palemoon/README.md
Normal file
5
test/palemoon/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Pale Moon CI tests
|
||||||
|
|
||||||
|
Pale Moon has exposed [some pretty bad bugs](https://anubis.techaro.lol/blog/release/v1.21.1#fix-event-loop-thrashing-when-solving-a-proof-of-work-challenge) in Anubis. As such, we're running Pale Moon against Anubis in CI to ensure that it keeps working.
|
||||||
|
|
||||||
|
This test is a fork of [dtinth/xtigervnc-docker](https://github.com/dtinth/xtigervnc-docker) but focused on Pale Moon.
|
||||||
50
test/palemoon/amd64/docker-compose.yml
Normal file
50
test/palemoon/amd64/docker-compose.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
services:
|
||||||
|
display:
|
||||||
|
image: ghcr.io/techarohq/ci-images/xserver:latest
|
||||||
|
pull_policy: always
|
||||||
|
ports:
|
||||||
|
- 5900:5900
|
||||||
|
|
||||||
|
anubis:
|
||||||
|
image: ko.local/anubis
|
||||||
|
environment:
|
||||||
|
BIND: ":3000"
|
||||||
|
TARGET: http://$TARGET
|
||||||
|
POLICY_FNAME: /cfg/anubis.yaml
|
||||||
|
SLOG_LEVEL: DEBUG
|
||||||
|
volumes:
|
||||||
|
- ../anubis:/cfg
|
||||||
|
depends_on:
|
||||||
|
- relayd
|
||||||
|
|
||||||
|
relayd:
|
||||||
|
image: ghcr.io/xe/x/relayd
|
||||||
|
environment:
|
||||||
|
BIND: :443
|
||||||
|
CERT_DIR: /techaro/pki
|
||||||
|
CERT_FNAME: cert.pem
|
||||||
|
KEY_FNAME: key.pem
|
||||||
|
PROXY_TO: http://anubis:3000
|
||||||
|
volumes:
|
||||||
|
- ../../pki/relayd:/techaro/pki:ro
|
||||||
|
|
||||||
|
# novnc:
|
||||||
|
# image: geek1011/easy-novnc
|
||||||
|
# command: -a :5800 -h display --no-url-password
|
||||||
|
# ports:
|
||||||
|
# - 5800:5800
|
||||||
|
|
||||||
|
palemoon:
|
||||||
|
platform: linux/amd64
|
||||||
|
init: true
|
||||||
|
image: ghcr.io/techarohq/ci-images/palemoon:latest
|
||||||
|
command: sleep inf
|
||||||
|
environment:
|
||||||
|
DISPLAY: display:0
|
||||||
|
volumes:
|
||||||
|
- ../../pki:/usr/local/share/ca-certificates/minica:ro
|
||||||
|
- ../scripts:/hack/scripts:ro
|
||||||
|
depends_on:
|
||||||
|
- anubis
|
||||||
|
- relayd
|
||||||
|
- display
|
||||||
30
test/palemoon/amd64/test.sh
Executable file
30
test/palemoon/amd64/test.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export VERSION=$GITHUB_COMMIT-test
|
||||||
|
export KO_DOCKER_REPO=ko.local
|
||||||
|
|
||||||
|
function capture_vnc_snapshots() {
|
||||||
|
sudo apt-get update && sudo apt-get install -y gvncviewer
|
||||||
|
mkdir -p ./var
|
||||||
|
while true; do
|
||||||
|
timestamp=$(date +"%Y%m%d%H%M%S")
|
||||||
|
gvnccapture localhost:0 ./var/snapshot_$timestamp.png 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
source ../../lib/lib.sh
|
||||||
|
|
||||||
|
if [ "$GITHUB_ACTIONS" = "true" ]; then
|
||||||
|
capture_vnc_snapshots &
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
build_anubis_ko
|
||||||
|
mint_cert relayd
|
||||||
|
|
||||||
|
go run ../../cmd/cipra/ --compose-name $(basename $(pwd))
|
||||||
|
|
||||||
|
docker compose down -t 1 || :
|
||||||
|
docker compose rm -f || :
|
||||||
2
test/palemoon/amd64/var/.gitignore
vendored
Normal file
2
test/palemoon/amd64/var/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
12
test/palemoon/anubis/anubis.yaml
Normal file
12
test/palemoon/anubis/anubis.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
bots:
|
||||||
|
- name: palemoon
|
||||||
|
user_agent_regex: PaleMoon
|
||||||
|
action: CHALLENGE
|
||||||
|
challenge:
|
||||||
|
difficulty: 2
|
||||||
|
report_as: 2
|
||||||
|
algorithm: fast
|
||||||
|
|
||||||
|
status_codes:
|
||||||
|
CHALLENGE: 401
|
||||||
|
DENY: 403
|
||||||
44
test/palemoon/i386/docker-compose.yml
Normal file
44
test/palemoon/i386/docker-compose.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
services:
|
||||||
|
display:
|
||||||
|
image: ghcr.io/techarohq/ci-images/xserver:latest
|
||||||
|
pull_policy: always
|
||||||
|
ports:
|
||||||
|
- 5900:5900
|
||||||
|
|
||||||
|
anubis:
|
||||||
|
image: ko.local/anubis
|
||||||
|
environment:
|
||||||
|
BIND: ":3000"
|
||||||
|
TARGET: http://$TARGET
|
||||||
|
POLICY_FNAME: /cfg/anubis.yaml
|
||||||
|
SLOG_LEVEL: DEBUG
|
||||||
|
volumes:
|
||||||
|
- ../anubis:/cfg
|
||||||
|
|
||||||
|
relayd:
|
||||||
|
image: ghcr.io/xe/x/relayd
|
||||||
|
environment:
|
||||||
|
BIND: :443
|
||||||
|
CERT_DIR: /techaro/pki
|
||||||
|
CERT_FNAME: cert.pem
|
||||||
|
KEY_FNAME: key.pem
|
||||||
|
PROXY_TO: http://anubis:3000
|
||||||
|
volumes:
|
||||||
|
- ../../pki/relayd:/techaro/pki:ro
|
||||||
|
|
||||||
|
# novnc:
|
||||||
|
# image: geek1011/easy-novnc
|
||||||
|
# command: -a :5800 -h display --no-url-password
|
||||||
|
# ports:
|
||||||
|
# - 5800:5800
|
||||||
|
|
||||||
|
palemoon:
|
||||||
|
platform: linux/386
|
||||||
|
init: true
|
||||||
|
image: ghcr.io/techarohq/ci-images/palemoon:latest
|
||||||
|
command: sleep inf
|
||||||
|
environment:
|
||||||
|
DISPLAY: display:0
|
||||||
|
volumes:
|
||||||
|
- ../../pki:/usr/local/share/ca-certificates/minica:ro
|
||||||
|
- ../scripts:/hack/scripts:ro
|
||||||
30
test/palemoon/i386/test.sh
Executable file
30
test/palemoon/i386/test.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export VERSION=$GITHUB_COMMIT-test
|
||||||
|
export KO_DOCKER_REPO=ko.local
|
||||||
|
|
||||||
|
function capture_vnc_snapshots() {
|
||||||
|
sudo apt-get update && sudo apt-get install -y gvncviewer
|
||||||
|
mkdir -p ./var
|
||||||
|
while true; do
|
||||||
|
timestamp=$(date +"%Y%m%d%H%M%S")
|
||||||
|
gvnccapture localhost:0 ./var/snapshot_$timestamp.png 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
source ../../lib/lib.sh
|
||||||
|
|
||||||
|
if [ "$GITHUB_ACTIONS" = "true" ]; then
|
||||||
|
capture_vnc_snapshots &
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
build_anubis_ko
|
||||||
|
mint_cert relayd
|
||||||
|
|
||||||
|
go run ../../cmd/cipra/ --compose-name $(basename $(pwd))
|
||||||
|
|
||||||
|
docker compose down -t 1 || :
|
||||||
|
docker compose rm -f || :
|
||||||
2
test/palemoon/i386/var/.gitignore
vendored
Normal file
2
test/palemoon/i386/var/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
103
test/palemoon/scripts/install-cert.sh
Executable file
103
test/palemoon/scripts/install-cert.sh
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CERT_PATH="/usr/local/share/ca-certificates/minica/minica.pem"
|
||||||
|
CERT_NAME="minica"
|
||||||
|
TRUST_FLAGS="C,,"
|
||||||
|
|
||||||
|
FIREFOX_DIR="$HOME/.mozilla/firefox"
|
||||||
|
PALEMOON_DIR="$HOME/.moonchild productions/pale moon"
|
||||||
|
|
||||||
|
echo "🔄 Updating system CA certificates..."
|
||||||
|
update-ca-certificates
|
||||||
|
|
||||||
|
# 🌀 Trigger Pale Moon to create its profile if needed
|
||||||
|
if command -v palemoon &>/dev/null; then
|
||||||
|
echo "🚀 Launching Pale Moon to initialize profile..."
|
||||||
|
palemoon &>/dev/null &
|
||||||
|
PALEMOON_PID=$!
|
||||||
|
|
||||||
|
# Wait up to 20 seconds for prefs.js to be created
|
||||||
|
for i in {1..20}; do
|
||||||
|
set +e
|
||||||
|
PROFILE_DIR=$(grep Path ~/.moonchild\ productions/pale\ moon/profiles.ini | cut -d= -f2)
|
||||||
|
PREFS_FILE="$HOME/.moonchild productions/pale moon/$PROFILE_DIR/prefs.js"
|
||||||
|
|
||||||
|
if [[ -f "$PREFS_FILE" ]]; then
|
||||||
|
set -e
|
||||||
|
echo "✅ prefs.js found at: $PREFS_FILE"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
kill $PALEMOON_PID 2>/dev/null || true
|
||||||
|
wait $PALEMOON_PID 2>/dev/null || true
|
||||||
|
|
||||||
|
if [[ ! -f "$PREFS_FILE" ]]; then
|
||||||
|
echo "❌ prefs.js not found. Pale Moon did not fully initialize."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠️ Pale Moon is not installed or not in PATH. Skipping profile bootstrap."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'user_pref("security.cert_pinning.enforcement_level", 0);' >>"$PREFS_FILE"
|
||||||
|
|
||||||
|
echo "✅ TLS cert validation disabled in Pale Moon profile: $PROFILE_DIR"
|
||||||
|
|
||||||
|
# 🔧 Ensure certutil is installed
|
||||||
|
if ! command -v certutil &>/dev/null; then
|
||||||
|
if [ -f /etc/debian_version ]; then
|
||||||
|
echo "🔧 'certutil' not found. Installing via apt..."
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y libnss3-tools
|
||||||
|
else
|
||||||
|
echo "❌ 'certutil' not found and install is only supported on Debian-based systems."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
import_cert_to_profiles() {
|
||||||
|
local base_dir="$1"
|
||||||
|
local browser_name="$2"
|
||||||
|
local profile_glob="$3"
|
||||||
|
|
||||||
|
if [ ! -d "$base_dir" ]; then
|
||||||
|
echo "⚠️ $browser_name profile directory not found: $base_dir"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📌 Searching for $browser_name profiles in: $base_dir"
|
||||||
|
|
||||||
|
local found=0
|
||||||
|
|
||||||
|
for profile in "$base_dir"/$profile_glob; do
|
||||||
|
if [ ! -d "$profile" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
found=1
|
||||||
|
local db_path="sql:$profile"
|
||||||
|
echo "🔍 Processing $browser_name profile: $profile"
|
||||||
|
|
||||||
|
if certutil -L -d "$db_path" | grep -q "^$CERT_NAME"; then
|
||||||
|
echo " ✅ Certificate '$CERT_NAME' already exists in profile."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
certutil -A -n "$CERT_NAME" -t "$TRUST_FLAGS" -i "$CERT_PATH" -d "$db_path"
|
||||||
|
echo " ➕ Added certificate '$CERT_NAME' to $browser_name profile."
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$found" -eq 0 ]; then
|
||||||
|
echo "⚠️ No $browser_name profiles found in: $base_dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
import_cert_to_profiles "$FIREFOX_DIR" "Firefox" "*.default*"
|
||||||
|
import_cert_to_profiles "$PALEMOON_DIR" "Pale Moon" "*.*"
|
||||||
|
|
||||||
|
echo "✅ Done. Firefox and Pale Moon profiles updated with '$CERT_NAME' certificate."
|
||||||
Reference in New Issue
Block a user