From bac1522c84fbe4f8fe37c4150b2b8b824903cdf7 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 26 May 2026 15:34:54 +0200 Subject: [PATCH 1/5] bump github.com/alecthomas/chroma/v2 from v2.24.1 to v2.25.0 Assisted-By: docker-agent --- go.mod | 3 ++- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f780da9f4..7cd28a214 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.1 github.com/Microsoft/go-winio v0.6.2 github.com/a2aproject/a2a-go v0.3.15 - github.com/alecthomas/chroma/v2 v2.24.1 + github.com/alecthomas/chroma/v2 v2.25.0 github.com/alpkeskin/gotoon v0.1.1 github.com/anthropics/anthropic-sdk-go v1.45.0 github.com/atotto/clipboard v0.1.4 @@ -86,6 +86,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect github.com/blevesearch/zapx/v17 v17.1.2 // indirect github.com/danieljoos/wincred v1.2.2 // indirect + github.com/dlclark/regexp2/v2 v2.1.0 // indirect github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect diff --git a/go.sum b/go.sum index 7fb84e3bd..277021752 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tj github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.24.1 h1:m5ffpfZbIb++k8AqFEKy9uVgY12xIQtBsQlc6DfZJQM= -github.com/alecthomas/chroma/v2 v2.24.1/go.mod h1:l+ohZ9xRXIbGe7cIW+YZgOGbvuVLjMps/FYN/CwuabI= +github.com/alecthomas/chroma/v2 v2.25.0 h1:DWkVlxrNpxPf+Qcfe04LBqUArxUiybK8ZQ9T7OFu68E= +github.com/alecthomas/chroma/v2 v2.25.0/go.mod h1:+95AZrRWlpW9g6qXD7S7UdHviopsGP/kCIrtJcU3QoQ= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -199,6 +199,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2/v2 v2.1.0 h1:jHXRmHRZGbuQzDZjMlCAXOvQb75iv3HyLDzXGj5H1AY= +github.com/dlclark/regexp2/v2 v2.1.0/go.mod h1:Bz5TMy5d8fPK0ximH0Yi9KvsRHNnvXqUx9XG6a4wB+I= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v29.5.2+incompatible h1:ubykJ1Y8LmNRGJ2BuMQ0kHOt/RO1YzGNswqWMJgivuQ= From c4ea1834acdf6480cd27d5683cfa81fb1a6e579d Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 26 May 2026 15:36:39 +0200 Subject: [PATCH 2/5] bump github.com/aws/aws-sdk-go-v2/config from v1.32.17 to v1.32.18 Also bumps github.com/aws/aws-sdk-go-v2/credentials from v1.19.16 to v1.19.17. Assisted-By: docker-agent --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7cd28a214..ed33ba2aa 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,8 @@ require ( github.com/anthropics/anthropic-sdk-go v1.45.0 github.com/atotto/clipboard v0.1.4 github.com/aws/aws-sdk-go-v2 v1.41.7 - github.com/aws/aws-sdk-go-v2/config v1.32.17 - github.com/aws/aws-sdk-go-v2/credentials v1.19.16 + github.com/aws/aws-sdk-go-v2/config v1.32.18 + github.com/aws/aws-sdk-go-v2/credentials v1.19.17 github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.52.0 github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 github.com/aws/smithy-go v1.25.1 @@ -121,7 +121,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect diff --git a/go.sum b/go.sum index 277021752..3ecb748b6 100644 --- a/go.sum +++ b/go.sum @@ -65,10 +65,10 @@ github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6t github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 h1:gx1AwW1Iyk9Z9dD9F4akX5gnN3QZwUB20GGKH/I+Rho= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10/go.mod h1:qqY157uZoqm5OXq/amuaBJyC9hgBCBQnsaWnPe905GY= -github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= -github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= +github.com/aws/aws-sdk-go-v2/config v1.32.18 h1:Hcia46bxhGgF3BaSnG8nSNCWmqTK6bj9xN9/FJ3WK6Q= +github.com/aws/aws-sdk-go-v2/config v1.32.18/go.mod h1:zEjCAYmxqDadH1WX8CdBvmLKhUEUVFgKRQG38zjDmrY= +github.com/aws/aws-sdk-go-v2/credentials v1.19.17 h1:gP2nkGsS+KMvF/jfFz2Vv2qiiOqWKyPACSzPsqHgoW8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.17/go.mod h1:Bsew3S/moG5iT77giPj1q8wb/s0RE5/QfH+ASjYtuQc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= @@ -87,8 +87,8 @@ github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VX github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.0 h1:nDARhv/oF55bcxF7rCI/4PDxOKnVXVWwDuDwCs2I2SQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.0/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= From 7a2bf3b7b6d26fae2da25c7362c153ecacfe4a82 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 26 May 2026 15:38:47 +0200 Subject: [PATCH 3/5] bump github.com/junegunn/fzf from v0.72.0 to v0.73.1 Assisted-By: docker-agent --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ed33ba2aa..d679a9db9 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/hashicorp/hcl/v2 v2.24.0 - github.com/junegunn/fzf v0.72.0 + github.com/junegunn/fzf v0.73.1 github.com/k3a/html2text v1.4.0 github.com/kofalt/go-memoize v0.0.0-20240506050413-9e5eb99a0f2a github.com/labstack/echo/v4 v4.15.2 diff --git a/go.sum b/go.sum index 3ecb748b6..e4433772f 100644 --- a/go.sum +++ b/go.sum @@ -330,8 +330,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/junegunn/fzf v0.72.0 h1:GnXEfKYp3s/HljrIcDgkOrWWxHWfhkLyRBIiC/enaVY= -github.com/junegunn/fzf v0.72.0/go.mod h1:xlXX2/rmsccKQUnr9QOXPDi5DyV9cM0UjKy/huScBeE= +github.com/junegunn/fzf v0.73.1 h1:Ni9jaVMxsehcx9IwJ4ZAL/sr2c4dOEg1DHIAwPUVlyo= +github.com/junegunn/fzf v0.73.1/go.mod h1:hKDkO8orBWT5VLIZOL8oyhxn7mRLXdGyu+gz1Ss58pU= github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741 h1:7dYDtfMDfKzjT+DVfIS4iqknSEKtZpEcXtu6vuaasHs= github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741/go.mod h1:6EILKtGpo5t+KLb85LNZLAF6P9LKp78hJI80PXMcn3c= github.com/k3a/html2text v1.4.0 h1:e4xarrVgZST+h+5C/fbA6AI49VFDSlEWMmIcDWcxsd0= From 65d8f29d4628dea871dedd159d73b65cbc0f301e Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 26 May 2026 15:42:25 +0200 Subject: [PATCH 4/5] bump github.com/docker/portcullis from v0.0.0-20260522094836-b66c4ab750fd to v0.0.0-20260526131538-fc97bf12bbdb The new version validates GitHub token CRC32 checksums, so the\nfake tokens used as redaction inputs in tests had to be regenerated\nwith correct base62 CRC32 suffixes. Assisted-By: docker-agent --- go.mod | 2 +- go.sum | 4 +-- pkg/hooks/builtins/redact_secrets_test.go | 27 +++++++++++++++---- pkg/sandbox/kit/kit_test.go | 33 ++++++++++++++++------- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index d679a9db9..361942701 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/coder/acp-go-sdk v0.13.0 github.com/docker/cli v29.5.2+incompatible github.com/docker/go-units v0.5.0 - github.com/docker/portcullis v0.0.0-20260522094836-b66c4ab750fd + github.com/docker/portcullis v0.0.0-20260526131538-fc97bf12bbdb github.com/dop251/goja v0.0.0-20260311135729-065cd970411c github.com/fatih/color v1.19.0 github.com/fsnotify/fsnotify v1.10.1 diff --git a/go.sum b/go.sum index e4433772f..a7aa2e9b0 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/portcullis v0.0.0-20260522094836-b66c4ab750fd h1:IGSoI9F8+p5ukGASbGrKpa0Mxm9CIGL6f/QzCgRzOhg= -github.com/docker/portcullis v0.0.0-20260522094836-b66c4ab750fd/go.mod h1:3Hov4sJxb5afp5R4NE9wSz/c8xlCTU9Kn1sUgF6O6U4= +github.com/docker/portcullis v0.0.0-20260526131538-fc97bf12bbdb h1:0Juktp2Xn6Nxs0fqaCeuTSM01i6Pe1+7aKLsBhMfp2I= +github.com/docker/portcullis v0.0.0-20260526131538-fc97bf12bbdb/go.mod h1:FBCDtWLlYquonR/uesgN9HhLvbaDIX3PEC6lgHCnL24= github.com/dop251/goja v0.0.0-20260311135729-065cd970411c h1:OcLmPfx1T1RmZVHHFwWMPaZDdRf0DBMZOFMVWJa7Pdk= github.com/dop251/goja v0.0.0-20260311135729-065cd970411c/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= diff --git a/pkg/hooks/builtins/redact_secrets_test.go b/pkg/hooks/builtins/redact_secrets_test.go index 7510e0002..c22f7731b 100644 --- a/pkg/hooks/builtins/redact_secrets_test.go +++ b/pkg/hooks/builtins/redact_secrets_test.go @@ -1,6 +1,7 @@ package builtins import ( + "hash/crc32" "testing" "github.com/docker/portcullis" @@ -12,6 +13,22 @@ import ( "github.com/docker/docker-agent/pkg/tools" ) +// fakeGitHubPAT returns a synthetic GitHub PAT with a body that +// passes portcullis' CRC32 checksum validation. The full token is +// never written as a source literal so GitHub's secret-scanning push +// protection doesn't flag this file. +func fakeGitHubPAT() string { + const body = "cxLeRrvbJfmYdUtr70xnNE3Q7Gvli4" + const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + var suffix [6]byte + checksum := uint64(crc32.ChecksumIEEE([]byte(body))) + for i := len(suffix) - 1; i >= 0; i-- { + suffix[i] = alphabet[checksum%62] + checksum /= 62 + } + return "ghp_" + body + string(suffix[:]) +} + // TestRedactSecretsScrubsTopLevelStringValue: a recognised secret in // a top-level string argument is replaced and ONLY the rewritten key // is emitted in UpdatedInput. The latter is critical because @@ -21,7 +38,7 @@ import ( func TestRedactSecretsScrubsTopLevelStringValue(t *testing.T) { t.Parallel() - const secret = "ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD" + secret := fakeGitHubPAT() in := &hooks.Input{ HookEventName: hooks.EventPreToolUse, @@ -133,7 +150,7 @@ func TestRedactSecretsIsRegistered(t *testing.T) { handler, ok := reg.LookupBuiltin(RedactSecrets) require.Truef(t, ok, "builtin %q must be registered", RedactSecrets) - const secret = "ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD" + secret := fakeGitHubPAT() out, err := handler(t.Context(), &hooks.Input{ HookEventName: hooks.EventPreToolUse, ToolName: "shell", @@ -187,7 +204,7 @@ func TestApplyAgentDefaultsInjectsRedactSecrets(t *testing.T) { func TestRedactSecretsScrubsOutgoingMessages(t *testing.T) { t.Parallel() - const secret = "ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD" + secret := fakeGitHubPAT() in := &hooks.Input{ HookEventName: hooks.EventBeforeLLMCall, Messages: []chat.Message{ @@ -236,7 +253,7 @@ func TestRedactSecretsBeforeLLMCallNoOpOnCleanMessages(t *testing.T) { func TestRedactSecretsBeforeLLMCallScrubsToolCallArguments(t *testing.T) { t.Parallel() - const secret = "ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD" + secret := fakeGitHubPAT() in := &hooks.Input{ HookEventName: hooks.EventBeforeLLMCall, Messages: []chat.Message{ @@ -275,7 +292,7 @@ func TestRedactSecretsBeforeLLMCallScrubsToolCallArguments(t *testing.T) { func TestRedactSecretsScrubsToolOutput(t *testing.T) { t.Parallel() - const secret = "ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD" + secret := fakeGitHubPAT() in := &hooks.Input{ HookEventName: hooks.EventToolResponseTransform, ToolName: "shell", diff --git a/pkg/sandbox/kit/kit_test.go b/pkg/sandbox/kit/kit_test.go index 8fde70c08..3c1eef2cf 100644 --- a/pkg/sandbox/kit/kit_test.go +++ b/pkg/sandbox/kit/kit_test.go @@ -2,6 +2,7 @@ package kit import ( "encoding/json" + "hash/crc32" "os" "path/filepath" "sort" @@ -18,10 +19,22 @@ import ( "github.com/docker/docker-agent/pkg/skills" ) -// fakeGitHubToken is a syntactically valid GitHub PAT that triggers -// portcullis. It is only used as input to the redactor, never as an -// actual credential. -const fakeGitHubToken = "ghp_" + "1234567890abcdefghijklmnopqrstuvwxyz" +// fakeGitHubToken returns a synthetic GitHub PAT that triggers +// portcullis. The trailing 6 chars are computed at runtime so the +// full token never appears as a source literal — otherwise GitHub's +// secret-scanning push protection would flag this file. Used only as +// input to the redactor, never as an actual credential. +func fakeGitHubToken() string { + const body = "1234567890abcdefghijklmnopqrst" + const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + var suffix [6]byte + checksum := uint64(crc32.ChecksumIEEE([]byte(body))) + for i := len(suffix) - 1; i >= 0; i-- { + suffix[i] = alphabet[checksum%62] + checksum /= 62 + } + return "ghp_" + body + string(suffix[:]) +} // isolateEnv prevents a developer-exported DOCKER_AGENT_KIT_DIR from // flipping the in-process resolvers (notably skills.Load) into @@ -39,7 +52,8 @@ func TestBuild_StagesSkillsAndRedacts(t *testing.T) { // Stage one local skill on the host with a secret embedded. skillDir := filepath.Join(hostHome, ".agents", "skills", "secret-keeper") require.NoError(t, os.MkdirAll(skillDir, 0o755)) - skillBody := "---\nname: secret-keeper\ndescription: ships with a secret\n---\n\ntoken=" + fakeGitHubToken + "\n" + token := fakeGitHubToken() + skillBody := "---\nname: secret-keeper\ndescription: ships with a secret\n---\n\ntoken=" + token + "\n" require.NoError(t, os.WriteFile(filepath.Join(skillDir, "SKILL.md"), []byte(skillBody), 0o644)) t.Setenv("HOME", hostHome) @@ -66,7 +80,7 @@ func TestBuild_StagesSkillsAndRedacts(t *testing.T) { staged := filepath.Join(res.HostDir, skills.KitSkillsSubdir, "secret-keeper", "SKILL.md") data, err := os.ReadFile(staged) require.NoError(t, err) - assert.NotContains(t, string(data), fakeGitHubToken, "host secret must not survive in kit") + assert.NotContains(t, string(data), token, "host secret must not survive in kit") assert.Contains(t, string(data), portcullis.Marker, "redaction marker must be present") // The manifest records the skill and the redaction. @@ -555,8 +569,9 @@ func TestPrintSummary(t *testing.T) { // with secret count, and ~ collapsing of host paths. withSecret := filepath.Join(hostHome, ".agents", "skills", "with-secret") require.NoError(t, os.MkdirAll(withSecret, 0o755)) + token := fakeGitHubToken() require.NoError(t, os.WriteFile(filepath.Join(withSecret, "SKILL.md"), - []byte("---\nname: with-secret\ndescription: leaks\n---\n\ntoken="+fakeGitHubToken+"\n"), 0o644)) + []byte("---\nname: with-secret\ndescription: leaks\n---\n\ntoken="+token+"\n"), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(withSecret, "helper.sh"), []byte("#!/bin/sh\necho hi\n"), 0o755)) @@ -566,7 +581,7 @@ func TestPrintSummary(t *testing.T) { []byte("---\nname: plain\ndescription: plain\n---\n"), 0o644)) agentsMD := filepath.Join(hostHome, "AGENTS.md") - require.NoError(t, os.WriteFile(agentsMD, []byte("token="+fakeGitHubToken+"\n"), 0o600)) + require.NoError(t, os.WriteFile(agentsMD, []byte("token="+token+"\n"), 0o600)) workspace := t.TempDir() agentYAML := []byte(`#!/usr/bin/env docker-agent @@ -622,7 +637,7 @@ models: assert.Contains(t, out, "summary: 2 skills, 1 prompt file, 2 secrets redacted") // And no host secret leaks into the printed output. - assert.NotContains(t, out, fakeGitHubToken) + assert.NotContains(t, out, token) } func TestPrintSummary_WorkspacePromptFile(t *testing.T) { From 69bed787a57a5ad63a70af93a6a3aebf5f17e2f5 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 26 May 2026 15:56:07 +0200 Subject: [PATCH 5/5] refactor(test): factor synthetic GitHub PAT helper into pkg/internal/portcullistest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both pkg/hooks/builtins/redact_secrets_test.go and pkg/sandbox/kit/ kit_test.go grew an identical CRC32-suffix computation to produce a synthetic, portcullis-detectable GitHub PAT without hard-coding the full 40-char ghp_ literal (which would trip GitHub secret-scanning push protection). Move that computation to a single helper — portcullistest.FakeGitHubPAT — parameterised by a 30-char body, and have both tests call it. --- pkg/hooks/builtins/redact_secrets_test.go | 16 ++------- pkg/internal/portcullistest/fakegithubpat.go | 33 +++++++++++++++++++ .../portcullistest/fakegithubpat_test.go | 28 ++++++++++++++++ pkg/sandbox/kit/kit_test.go | 19 +++-------- 4 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 pkg/internal/portcullistest/fakegithubpat.go create mode 100644 pkg/internal/portcullistest/fakegithubpat_test.go diff --git a/pkg/hooks/builtins/redact_secrets_test.go b/pkg/hooks/builtins/redact_secrets_test.go index c22f7731b..f4966b047 100644 --- a/pkg/hooks/builtins/redact_secrets_test.go +++ b/pkg/hooks/builtins/redact_secrets_test.go @@ -1,7 +1,6 @@ package builtins import ( - "hash/crc32" "testing" "github.com/docker/portcullis" @@ -10,23 +9,12 @@ import ( "github.com/docker/docker-agent/pkg/chat" "github.com/docker/docker-agent/pkg/hooks" + "github.com/docker/docker-agent/pkg/internal/portcullistest" "github.com/docker/docker-agent/pkg/tools" ) -// fakeGitHubPAT returns a synthetic GitHub PAT with a body that -// passes portcullis' CRC32 checksum validation. The full token is -// never written as a source literal so GitHub's secret-scanning push -// protection doesn't flag this file. func fakeGitHubPAT() string { - const body = "cxLeRrvbJfmYdUtr70xnNE3Q7Gvli4" - const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - var suffix [6]byte - checksum := uint64(crc32.ChecksumIEEE([]byte(body))) - for i := len(suffix) - 1; i >= 0; i-- { - suffix[i] = alphabet[checksum%62] - checksum /= 62 - } - return "ghp_" + body + string(suffix[:]) + return portcullistest.FakeGitHubPAT("cxLeRrvbJfmYdUtr70xnNE3Q7Gvli4") } // TestRedactSecretsScrubsTopLevelStringValue: a recognised secret in diff --git a/pkg/internal/portcullistest/fakegithubpat.go b/pkg/internal/portcullistest/fakegithubpat.go new file mode 100644 index 000000000..81f8e97b5 --- /dev/null +++ b/pkg/internal/portcullistest/fakegithubpat.go @@ -0,0 +1,33 @@ +// Package portcullistest provides test helpers for code that +// exercises portcullis' GitHub-token detection rules. +package portcullistest + +import "hash/crc32" + +// FakeGitHubPAT returns a synthetic GitHub PAT whose trailing 6-char +// base62 CRC32 suffix passes portcullis' validGitHubChecksum, so the +// returned token is detected (and redacted) by the secret scanner. +// +// The token is assembled at runtime: the full 40-char `ghp_…` string +// never appears as a source literal, so GitHub's secret-scanning +// push protection won't flag files that call this helper. +// +// body must be exactly 30 base62 characters; callers vary it to get +// distinct fake tokens. The result has the GitHub-PAT shape +// `ghp_` + 36 base62 chars and is never a real credential. +func FakeGitHubPAT(body string) string { + const ( + alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + suffixLen = 6 + ) + if len(body) != 30 { + panic("portcullistest: body must be 30 chars") + } + var suffix [suffixLen]byte + checksum := uint64(crc32.ChecksumIEEE([]byte(body))) + for i := suffixLen - 1; i >= 0; i-- { + suffix[i] = alphabet[checksum%62] + checksum /= 62 + } + return "ghp_" + body + string(suffix[:]) +} diff --git a/pkg/internal/portcullistest/fakegithubpat_test.go b/pkg/internal/portcullistest/fakegithubpat_test.go new file mode 100644 index 000000000..94948523b --- /dev/null +++ b/pkg/internal/portcullistest/fakegithubpat_test.go @@ -0,0 +1,28 @@ +package portcullistest_test + +import ( + "strings" + "testing" + + "github.com/docker/portcullis" + "github.com/stretchr/testify/assert" + + "github.com/docker/docker-agent/pkg/internal/portcullistest" +) + +func TestFakeGitHubPAT_DetectedByPortcullis(t *testing.T) { + t.Parallel() + + tok := portcullistest.FakeGitHubPAT("cxLeRrvbJfmYdUtr70xnNE3Q7Gvli4") + + assert.True(t, strings.HasPrefix(tok, "ghp_")) + assert.Len(t, tok, 40) + assert.True(t, portcullis.Contains(tok), "synthetic PAT must trigger portcullis detection") + assert.Equal(t, portcullis.Marker, portcullis.Redact(tok)) +} + +func TestFakeGitHubPAT_RejectsWrongLength(t *testing.T) { + t.Parallel() + + assert.Panics(t, func() { portcullistest.FakeGitHubPAT("too-short") }) +} diff --git a/pkg/sandbox/kit/kit_test.go b/pkg/sandbox/kit/kit_test.go index 3c1eef2cf..9ac438ac0 100644 --- a/pkg/sandbox/kit/kit_test.go +++ b/pkg/sandbox/kit/kit_test.go @@ -2,7 +2,6 @@ package kit import ( "encoding/json" - "hash/crc32" "os" "path/filepath" "sort" @@ -15,25 +14,15 @@ import ( "github.com/stretchr/testify/require" latestcfg "github.com/docker/docker-agent/pkg/config/latest" + "github.com/docker/docker-agent/pkg/internal/portcullistest" "github.com/docker/docker-agent/pkg/promptfiles" "github.com/docker/docker-agent/pkg/skills" ) -// fakeGitHubToken returns a synthetic GitHub PAT that triggers -// portcullis. The trailing 6 chars are computed at runtime so the -// full token never appears as a source literal — otherwise GitHub's -// secret-scanning push protection would flag this file. Used only as -// input to the redactor, never as an actual credential. +// fakeGitHubToken is used only as input to the redactor, never as an +// actual credential. func fakeGitHubToken() string { - const body = "1234567890abcdefghijklmnopqrst" - const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - var suffix [6]byte - checksum := uint64(crc32.ChecksumIEEE([]byte(body))) - for i := len(suffix) - 1; i >= 0; i-- { - suffix[i] = alphabet[checksum%62] - checksum /= 62 - } - return "ghp_" + body + string(suffix[:]) + return portcullistest.FakeGitHubPAT("1234567890abcdefghijklmnopqrst") } // isolateEnv prevents a developer-exported DOCKER_AGENT_KIT_DIR from