docs: preserve reviewed NetKingdom session artifacts
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Reject generated-only commits / generated-commit-guard (push) Waiting to run

This commit is contained in:
codex 2026-11-01 23:19:29 +01:00
parent d6168526c5
commit d9a74861ba
44 changed files with 18977 additions and 0 deletions

View file

@ -0,0 +1,708 @@
[
{
"operation": [
"migrate"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused-debug-logging",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/CLIENT_SECRET"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"synthetic-oauth",
"--provider",
"github",
"--key",
"synthetic-public-id",
"--secret-file",
"/run/forgejo-oidc/CLIENT_SECRET",
"--group-claim-name",
"groups",
"--admin-group",
"unchanged-admin",
"--scopes",
"read:user,user:email"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--icon-url",
"https://synthetic.invalid/icon"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--secret-file",
"/run/forgejo-oidc/replacement"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"synthetic-guarded"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--secret-file",
"/run/forgejo-oidc/good"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/newline"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/newline"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/short"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/short"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/long"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/long"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/badbyte"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/badbyte"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/world"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/world"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/writable"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/writable"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/executable"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/executable"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/empty"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/empty"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/symlink"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/symlink"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/fifo"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/fifo"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/directory"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/directory"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/missing"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/missing"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/badparent/value"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/badparent/value"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/parentlink/value"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/parentlink/value"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"relative"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"relative"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
""
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
""
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/../forgejo-oidc/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/forgejo-oidc/../forgejo-oidc/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--provider",
"github",
"--secret-file",
"/run/writable-input/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--name",
"refused",
"--secret-file",
"/run/writable-input/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused",
"--secret",
"",
"--secret-file",
"/run/forgejo-oidc/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--secret",
"",
"--secret-file",
"/run/forgejo-oidc/good"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--required-claim-values",
""
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--secret-file",
"/run/forgejo-oidc/replacement"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"update-oauth",
"--id",
"1",
"--secret",
"synthetic-legacy-literal"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
}
]

View file

@ -0,0 +1,97 @@
{
"returncode": 0,
"seconds": 32.57,
"argv": [
"bwrap",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-forgejo-successor-attempt2",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/cache/download",
"/cached",
"--ro-bind",
"/etc/ssl/certs",
"/etc/ssl/certs",
"--ro-bind",
"/etc/resolv.conf",
"/etc/resolv.conf",
"--chdir",
"/work/source",
"--clearenv",
"--setenv",
"PATH",
"/work/go/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOPATH",
"/work/gopath",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"file:///cached,https://proxy.golang.org",
"--setenv",
"GOSUMDB",
"sum.golang.org",
"--setenv",
"GOVCS",
"*:off",
"--setenv",
"GOMODCACHE",
"/work/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"1",
"--setenv",
"GIT_CONFIG_NOSYSTEM",
"1",
"--setenv",
"GIT_CONFIG_GLOBAL",
"/dev/null",
"--setenv",
"GIT_TERMINAL_PROMPT",
"0",
"--",
"go",
"mod",
"download"
],
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
"module_hashes": {
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
},
"network": "public module download only",
"pid_namespace": "private",
"real_credentials": false
}

View file

@ -0,0 +1,156 @@
{
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
"inputs_verified": true,
"paths": [
{
"packet": "forgejo-required-claim-values-r1",
"path": "go.mod",
"sha256": "652162a4a95c6d1c349d2a8c0b514def5abd837f001137b77b7b3f4a0f162d00"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "cmd/admin_auth_oauth.go",
"sha256": "1dd03f0bd23889a340a10fcc18d2e3a49270b79056aafd1b3baf01b18559b37f"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "services/forms/auth_form.go",
"sha256": "026720f23cbdb5e54f8c594109c2dc306b87e323cc649df5fa3194f609cd7737"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "services/auth/source/oauth2/required_claim_values_test.go",
"sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "services/auth/source/oauth2/required_claim_values.go",
"sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "services/auth/source/oauth2/providers_openid.go",
"sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "services/auth/source/oauth2/source.go",
"sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "routers/web/admin/auths.go",
"sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923"
},
{
"packet": "forgejo-required-claim-values-r1",
"path": "routers/web/auth/oauth.go",
"sha256": "081bcb94023d6e27289196bbe518bebba0a8a6082ea1a32d2794c5562075907b"
},
{
"packet": "forgejo-private-input-r1",
"path": "cmd/admin_auth_oauth.go",
"sha256": "c01084dd631937e3cd1d08593e7090d44661c6cba2838e217bdadbbc5f3c29d9"
},
{
"packet": "forgejo-private-input-r1",
"path": "cmd/admin_auth_oauth_secret_linux.go",
"sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b"
},
{
"packet": "forgejo-private-input-r1",
"path": "cmd/admin_auth_oauth_secret_other.go",
"sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d"
},
{
"packet": "forgejo-private-input-r1",
"path": "cmd/admin_auth_oauth_secret_linux_test.go",
"sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2"
},
{
"packet": "forgejo-private-input-r1",
"path": "models/db/engine.go",
"sha256": "43683804d0c5bc6ead1dba0344c69dd196825cf3d5eb502af8d932a7b8207b96"
},
{
"packet": "forgejo-private-input-r1",
"path": "models/db/sensitive_sql.go",
"sha256": "8d4e6b3f08e9fdd919e97e47bd857cbd8935fdcbf367d20b5b8c545f279e796e"
},
{
"packet": "forgejo-private-input-r1",
"path": "models/db/sensitive_sql_test.go",
"sha256": "f0f74c09a9e96a6529db7b9ce459a2ea37e204de38c5ad01855b80c9a992a769"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/db/engine.go",
"sha256": "43683804d0c5bc6ead1dba0344c69dd196825cf3d5eb502af8d932a7b8207b96"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/db/context.go",
"sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/db/sensitive_sql.go",
"sha256": "8d4e6b3f08e9fdd919e97e47bd857cbd8935fdcbf367d20b5b8c545f279e796e"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/db/sensitive_sql_test.go",
"sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/auth/source_operation_internal_test.go",
"sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/auth/source_operation_test.go",
"sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/auth/source_operation.go",
"sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31"
},
{
"packet": "apps-operation-backend-r1",
"path": "models/auth/source_operation_process_test.go",
"sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "go.mod",
"sha256": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "services/auth/source/oauth2/access_engine_test.go",
"sha256": "1bbc4a823cdf93005d72a6db2eb77e0096c80903fbdf247a385431d70b9620da"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "services/auth/source/oauth2/source_callout.go",
"sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "services/auth/source/oauth2/access_engine_callback_test.go",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "services/auth/source/oauth2/access_engine.go",
"sha256": "c1f2b4c76d09687b9b66dd13b3ba671ba92ae72f26751f8c273607ab9be65b07"
},
{
"packet": "apps-access-engine-integration-r2",
"path": "routers/web/auth/oauth.go",
"sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17"
}
]
}

View file

@ -0,0 +1,109 @@
{
"returncode": 1,
"seconds": 500.05,
"argv": [
"bwrap",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--unshare-net",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-forgejo-successor-attempt2",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/cache/download",
"/cached",
"--ro-bind",
"/etc/ssl/certs",
"/etc/ssl/certs",
"--ro-bind",
"/etc/resolv.conf",
"/etc/resolv.conf",
"--chdir",
"/work/source",
"--clearenv",
"--setenv",
"PATH",
"/work/go/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOPATH",
"/work/gopath",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"off",
"--setenv",
"GOSUMDB",
"off",
"--setenv",
"GOVCS",
"*:off",
"--setenv",
"GOMODCACHE",
"/work/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"1",
"--setenv",
"GIT_CONFIG_NOSYSTEM",
"1",
"--setenv",
"GIT_CONFIG_GLOBAL",
"/dev/null",
"--setenv",
"GIT_TERMINAL_PROMPT",
"0",
"--",
"go",
"test",
"-p",
"4",
"-json",
"-tags",
"sqlite sqlite_unlock_notify",
"./services/auth/source/oauth2",
"./cmd",
"./models/auth",
"./models/db",
"-run",
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQLHooks",
"-count=1"
],
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
"module_hashes": {
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
},
"network": "disabled",
"pid_namespace": "private",
"real_credentials": false
}

View file

@ -0,0 +1,117 @@
{
"returncode": 0,
"seconds": 21.03,
"argv": [
"bwrap",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--unshare-net",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-forgejo-successor-attempt2",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/cache/download",
"/cached",
"--ro-bind",
"/etc/ssl/certs",
"/etc/ssl/certs",
"--ro-bind",
"/etc/resolv.conf",
"/etc/resolv.conf",
"--chdir",
"/work/source",
"--clearenv",
"--dir",
"/run",
"--ro-bind",
"/tmp/custodian-forgejo-successor-attempt2/descriptor-fixtures",
"/run/forgejo-oidc",
"--bind",
"/tmp/custodian-forgejo-successor-attempt2/descriptor-fixtures",
"/run/writable-input",
"--setenv",
"PATH",
"/work/go/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOPATH",
"/work/gopath",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"off",
"--setenv",
"GOSUMDB",
"off",
"--setenv",
"GOVCS",
"*:off",
"--setenv",
"GOMODCACHE",
"/work/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"1",
"--setenv",
"GIT_CONFIG_NOSYSTEM",
"1",
"--setenv",
"GIT_CONFIG_GLOBAL",
"/work/home/test.gitconfig",
"--setenv",
"GIT_TERMINAL_PROMPT",
"0",
"--",
"go",
"test",
"-p",
"1",
"-json",
"-tags",
"sqlite sqlite_unlock_notify",
"./services/auth/source/oauth2",
"./cmd",
"./models/auth",
"./models/db",
"-run",
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQLHooks",
"-count=1"
],
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
"module_hashes": {
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
},
"network": "disabled",
"pid_namespace": "private",
"real_credentials": false
}

View file

@ -0,0 +1,193 @@
---
id: forgejo.application-access
name: Forgejo authenticated application access
namespace: forgejo:application
version: v1
status: draft
package: flexauth.forgejo.application
allow_ttl: 10s
actions: [application.access]
owner: railiance-apps
caring:
profile: caring-0.4.0-rc2
enforce: false
---
This source policy enforces the original three-group any-of in the sole PDP.
The registry identifies the application tenant and admitted issuer. Fresh identity
and UserInfo facts arrive only in request context from the admitted Forgejo caller,
which verifies issuer JOSE and current UserInfo before this request. They are not
static subject registry attributes. This policy requires aal2 assurance.
The PEP independently verifies the ID token and supplies fresh UserInfo groups;
the authenticated workload caller may represent only the Forgejo system. A
decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.
The accompanying registry is synthetic test data, not an estate admission.
```rego
import rego.v1
default decision := {"effect": "deny", "reason": "application_access_refused"}
original_groups := {"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
decision := {"effect": "allow", "reason": "current_authenticated_application_access"} if {
input.action == "application.access"
object.get(input, "purpose", "") == ""
input.resource.id == "application:forgejo"
input.resource.type == "application"
input.resource.system == "forgejo"
input.tenant == input.resource.tenant
input.tenant == input.resource.attributes.tenant
input.subject.tenant == input.tenant
input.context.identity_tenant == input.tenant
input.subject.id == input.context.identity_subject
input.subject.type == input.context.identity_principal_type
input.subject.type == "human"
input.context.identity_issuer == input.resource.attributes.identity_issuer
input.context.identity_assurance == "aal2"
count(input.context.required_groups_any_of) == 3
{g | g := input.context.required_groups_any_of[_]} == original_groups
some group in input.context.current_userinfo_groups
group in original_groups
is_string(input.context.decisionpep_request_nonce)
count(input.context.decisionpep_request_nonce) == 64
}
```
```rego test
package flexauth.forgejo.application_test
import rego.v1
import data.flexauth.forgejo.application
valid_request := {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {
"identity_tenant": "tenant:synthetic",
"identity_subject": "user:synthetic",
"identity_principal_type": "human",
"identity_issuer": "https://issuer.invalid",
"identity_assurance": "aal2",
"current_userinfo_groups": [
"forge-admins"
],
"required_groups_any_of": [
"forge-admins",
"forge-coulomb-owners",
"forge-coulomb-readers"
],
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
test_original_groups_allow if {
every group in ["forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"] {
application.decision.effect == "allow" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": [group]})})
}
}
test_outsider_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": ["outsider"]})})
}
test_missing_identity_denied if {
application.decision.effect == "deny" with input as object.union(object.remove(valid_request, ["context"]), {"context": object.remove(valid_request.context, ["identity_issuer"])})
}
test_low_assurance_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"identity_assurance": "aal1"})})
}
test_changed_group_contract_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"required_groups_any_of": ["outsider"]})})
}
```
```yaml fixture
[
{
"id": "allowed-original-group",
"request": {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {
"identity_tenant": "tenant:synthetic",
"identity_subject": "user:synthetic",
"identity_principal_type": "human",
"identity_issuer": "https://issuer.invalid",
"identity_assurance": "aal2",
"current_userinfo_groups": [
"forge-admins"
],
"required_groups_any_of": [
"forge-admins",
"forge-coulomb-owners",
"forge-coulomb-readers"
],
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"expect": {
"effect": "allow",
"reason": "current_authenticated_application_access"
}
},
{
"id": "missing-identity-context",
"request": {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {}
},
"expect": {
"effect": "deny",
"reason": "application_access_refused"
}
}
]
```

View file

@ -0,0 +1,47 @@
[
{
"operation": [
"migrate"
],
"returncode": 0,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"refused-debug-logging",
"--provider",
"github",
"--secret-file",
"/run/forgejo-oidc/CLIENT_SECRET"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
},
{
"operation": [
"admin",
"auth",
"add-oauth",
"--name",
"synthetic-oauth",
"--provider",
"github",
"--key",
"synthetic-public-id",
"--secret-file",
"/run/forgejo-oidc/CLIENT_SECRET",
"--group-claim-name",
"groups",
"--admin-group",
"unchanged-admin",
"--scopes",
"read:user,user:email"
],
"returncode": 1,
"stdout_stderr_secret_absent": true
}
]

View file

@ -0,0 +1,99 @@
import pathlib,subprocess,json,sqlite3,os,shlex,time
w=pathlib.Path('/tmp/custodian-forgejo-successor-attempt2/cli-work');s='/tmp/forgejo-private-input-r1';r='/tmp/forgejo-independent-r1'
fixtures=w/'fixtures'; fixtures.mkdir(exist_ok=True)
A='AbCdEf012345678-'*4;B='ZyXwVu_987654321'*4 # synthetic exact64
assert len(A)==64 and len(B)==64
for name,data,mode in [('good',A,0o400),('replacement',B,0o440),('newline',A+'\n',0o400),('short',A[:-1],0o400),('long',A+'A',0o400),('badbyte',A[:-1]+'=',0o400),('world',A,0o444),('writable',A,0o600),('executable',A,0o500),('empty','',0o400),('CLIENT_SECRET','',0o400)]:
p=fixtures/name
if p.exists():p.chmod(0o600)
p.write_text(data);p.chmod(mode)
for name in ['symlink']:
p=fixtures/name
if not p.exists():p.symlink_to('good')
if not (fixtures/'fifo').exists():os.mkfifo(fixtures/'fifo',0o400)
(fixtures/'directory').mkdir(exist_ok=True)
(fixtures/'badparent').mkdir(exist_ok=True);(fixtures/'badparent').chmod(0o777)
if (fixtures/'badparent/value').exists():(fixtures/'badparent/value').chmod(0o600)
(fixtures/'badparent/value').write_text(A);(fixtures/'badparent/value').chmod(0o400)
if not (fixtures/'parentlink').exists():(fixtures/'parentlink').symlink_to('directory',target_is_directory=True)
(w/'runtime').mkdir(exist_ok=True)
dbfile=w/'runtime/test.db'
if dbfile.exists():dbfile.unlink() # isolated synthetic test database only
ini=w/'runtime/app.ini';ini.write_text('''APP_NAME = synthetic-private-input-test
RUN_USER = worsch
WORK_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime
[security]
INSTALL_LOCK = true
SECRET_KEY = SYNTHETIC-TEST-KEY
[database]
DB_TYPE = sqlite3
SLOW_QUERY_THRESHOLD = 1ns
PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/test.db
[server]
APP_DATA_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/data
OFFLINE_MODE = true
[repository]
ROOT = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/repos
[log]
MODE = console
LEVEL = Error
''')
env={'PATH':'/usr/bin:/bin','HOME':str(w/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':str(w/'home/test.gitconfig'),'GIT_TERMINAL_PROMPT':'0','USER':'worsch','GITEA_WORK_DIR':str(w/'runtime')}
base=['bwrap','--unshare-pid','--unshare-net','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib','--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev','--tmpfs','/tmp','--bind',str(w),str(w),'--dir','/run','--ro-bind',str(fixtures),'/run/forgejo-oidc','--ro-bind',str(fixtures/'good'),'/run/forgejo-oidc/CLIENT_SECRET','--bind',str(fixtures),'/run/writable-input','--chdir',str(w/'runtime'),'--','env','-i']+[k+'='+v for k,v in env.items()]+[str(w/'forgejo'),'--config',str(ini)]
results=[]
def run(args,ok=True,reason=None):
p=subprocess.run(base+args,capture_output=True,text=True,timeout=60); output=p.stdout+p.stderr
assert A not in output and B not in output, 'synthetic input leaked in output'
assert all(A not in arg and B not in arg for arg in base+args), 'synthetic input in argv'
assert all(A not in v and B not in v for v in env.values()), 'synthetic input in env'
results.append({'operation':args,'returncode':p.returncode,'stdout_stderr_secret_absent':True})
(w/'cli-attempts.json').write_text(json.dumps(results,indent=2))
if (p.returncode==0)!=ok:raise AssertionError('CLI unexpected status: '+repr(args)+'\n'+output[-5000:])
if reason:assert reason in output,output[-2000:]
return p
run(['migrate'])
clean_ini=ini.read_text()
ini.write_text(clean_ini.replace('DB_TYPE = sqlite3', 'DB_TYPE = sqlite3\nLOG_SQL = true'))
run(['admin','auth','add-oauth','--name','refused-debug-logging','--provider','github','--secret-file','/run/forgejo-oidc/CLIENT_SECRET'],False,'OAuth configuration requires SQL logging disabled')
ini.write_text(clean_ini)
def rows():
with sqlite3.connect(w/'runtime/test.db') as db:return db.execute('select id,name,cfg from login_source order by id').fetchall()
def cfg():return json.loads(rows()[0][2])
run(['admin','auth','add-oauth','--name','synthetic-oauth','--provider','github','--key','synthetic-public-id','--secret-file','/run/forgejo-oidc/CLIENT_SECRET','--group-claim-name','groups','--admin-group','unchanged-admin','--scopes','read:user,user:email'])
assert len(rows())==1 and cfg()['ClientSecret']==A
id=str(rows()[0][0]); before=cfg()
run(['admin','auth','update-oauth','--id',id,'--icon-url','https://synthetic.invalid/icon'])
assert cfg()['ClientSecret']==A
preserved=cfg()
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'])
after=cfg();assert after['ClientSecret']==B
assert {k:v for k,v in after.items() if k!='ClientSecret'}=={k:v for k,v in preserved.items() if k!='ClientSecret'},'secret replacement changed unrelated config'
# Guarded inactive source exercises actual update omission and strict list semantics
# without discovery/network or claiming a browser login.
with sqlite3.connect(w/'runtime/test.db') as db:
guarded=after|{'Provider':'openidConnect','OpenIDConnectAutoDiscoveryURL':'https://synthetic.invalid/.well-known/openid-configuration','RequiredClaimName':'groups','RequiredClaimValues':['forge-admins','forge-coulomb-owners','forge-coulomb-readers']}
db.execute('update login_source set is_active=0,cfg=? where id=?',(json.dumps(guarded),id))
run(['admin','auth','update-oauth','--id',id,'--name','synthetic-guarded'])
assert cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] and cfg()['ClientSecret']==B
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/good'])
assert cfg()['ClientSecret']==A and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
for path in ['/run/forgejo-oidc/'+n for n in ['newline','short','long','badbyte','world','writable','executable','empty','symlink','fifo','directory','missing','badparent/value','parentlink/value']]+['relative','','/run/forgejo-oidc/../forgejo-oidc/good','/run/writable-input/good']:
before=rows()
for cmd in [['admin','auth','add-oauth','--name','refused','--provider','github'],['admin','auth','update-oauth','--id',id,'--name','refused']]:
run(cmd+['--secret-file',path],False,'secret-file input refused')
assert rows()==before,'failure changed DB'
for action in [['add-oauth','--name','refused'],['update-oauth','--id',id]]:
before=rows();run(['admin','auth']+action+['--secret','','--secret-file','/run/forgejo-oidc/good'],False,'mutually exclusive');assert rows()==before
# Failure to clear/ambiguously replace gate keeps stored application bytes unchanged.
before=rows();run(['admin','auth','update-oauth','--id',id,'--required-claim-values',''],False);assert rows()==before
# Actual XORM error hook under a failing SQLite trigger must not disclose cfg.
before=rows()
with sqlite3.connect(w/'runtime/test.db') as database:
database.execute("create trigger synthetic_private_error before update on login_source begin select raise(abort, 'synthetic-db-failure'); end")
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'],False,'OAuth source update failed')
assert rows()==before
with sqlite3.connect(w/'runtime/test.db') as database:database.execute('drop trigger synthetic_private_error')
# Legacy explicit secret remains supported; synthetic legacy string is not private input.
run(['admin','auth','update-oauth','--id',id,'--secret','synthetic-legacy-literal'])
assert cfg()['ClientSecret']=='synthetic-legacy-literal' and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
(w/'cli-results.json').write_text(json.dumps({'passed':True,'subprocesses':len(results),'real_backend_cli_and_sqlite':True,'network':'disabled by bwrap','secret_inputs':'synthetic only','values_never_in_argv_env_output':True,'actual_browser_login':False,'actual_readonly_file_bind':True,'replacement_preserves_other_config':True,'unsafe_SQL_logging_refused':True,'actual_error_and_slow_hooks_no_leak':True},indent=2));print('CLI integrations passed:',len(results))

View file

@ -0,0 +1,193 @@
---
id: forgejo.application-access
name: Forgejo authenticated application access
namespace: forgejo:application
version: v1
status: draft
package: flexauth.forgejo.application
allow_ttl: 10s
actions: [application.access]
owner: railiance-apps
caring:
profile: caring-0.4.0-rc2
enforce: false
---
This source policy enforces the original three-group any-of in the sole PDP.
The registry identifies the application tenant and admitted issuer. Fresh identity
and UserInfo facts arrive only in request context from the admitted Forgejo caller,
which verifies issuer JOSE and current UserInfo before this request. They are not
static subject registry attributes. This policy requires aal2 assurance.
The PEP independently verifies the ID token and supplies fresh UserInfo groups;
the authenticated workload caller may represent only the Forgejo system. A
decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.
The accompanying registry is synthetic test data, not an estate admission.
```rego
import rego.v1
default decision := {"effect": "deny", "reason": "application_access_refused"}
original_groups := {"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
decision := {"effect": "allow", "reason": "current_authenticated_application_access"} if {
input.action == "application.access"
object.get(input, "purpose", "") == ""
input.resource.id == "application:forgejo"
input.resource.type == "application"
input.resource.system == "forgejo"
input.tenant == input.resource.tenant
input.tenant == input.resource.attributes.tenant
input.subject.tenant == input.tenant
input.context.identity_tenant == input.tenant
input.subject.id == input.context.identity_subject
input.subject.type == input.context.identity_principal_type
input.subject.type == "human"
input.context.identity_issuer == input.resource.attributes.identity_issuer
input.context.identity_assurance == "aal2"
count(input.context.required_groups_any_of) == 3
{g | g := input.context.required_groups_any_of[_]} == original_groups
some group in input.context.current_userinfo_groups
group in original_groups
is_string(input.context.decisionpep_request_nonce)
count(input.context.decisionpep_request_nonce) == 64
}
```
```rego test
package flexauth.forgejo.application_test
import rego.v1
import data.flexauth.forgejo.application
valid_request := {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {
"identity_tenant": "tenant:synthetic",
"identity_subject": "user:synthetic",
"identity_principal_type": "human",
"identity_issuer": "https://issuer.invalid",
"identity_assurance": "aal2",
"current_userinfo_groups": [
"forge-admins"
],
"required_groups_any_of": [
"forge-admins",
"forge-coulomb-owners",
"forge-coulomb-readers"
],
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
test_original_groups_allow if {
every group in ["forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"] {
application.decision.effect == "allow" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": [group]})})
}
}
test_outsider_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": ["outsider"]})})
}
test_missing_identity_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.remove(valid_request.context, ["identity_issuer"])})
}
test_low_assurance_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"identity_assurance": "aal1"})})
}
test_changed_group_contract_denied if {
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"required_groups_any_of": ["outsider"]})})
}
```
```yaml fixture
[
{
"id": "allowed-original-group",
"request": {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {
"identity_tenant": "tenant:synthetic",
"identity_subject": "user:synthetic",
"identity_principal_type": "human",
"identity_issuer": "https://issuer.invalid",
"identity_assurance": "aal2",
"current_userinfo_groups": [
"forge-admins"
],
"required_groups_any_of": [
"forge-admins",
"forge-coulomb-owners",
"forge-coulomb-readers"
],
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"expect": {
"effect": "allow",
"reason": "current_authenticated_application_access"
}
},
{
"id": "missing-identity-context",
"request": {
"tenant": "tenant:synthetic",
"subject": {
"id": "user:synthetic",
"type": "human",
"tenant": "tenant:synthetic"
},
"action": "application.access",
"resource": {
"id": "application:forgejo",
"type": "application",
"system": "forgejo",
"tenant": "tenant:synthetic",
"attributes": {
"tenant": "tenant:synthetic",
"identity_issuer": "https://issuer.invalid"
}
},
"context": {}
},
"expect": {
"effect": "deny",
"reason": "application_access_refused"
}
}
]
```

View file

@ -0,0 +1,368 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"errors"
"fmt"
"net/url"
auth_model "forgejo.org/models/auth"
"forgejo.org/models/db"
"forgejo.org/services/auth/source/oauth2"
"github.com/urfave/cli/v2"
)
var (
oauthCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
&cli.StringFlag{
Name: "provider",
Value: "",
Usage: "OAuth2 Provider",
},
&cli.StringFlag{
Name: "key",
Value: "",
Usage: "Client ID (Key)",
},
&cli.StringFlag{
Name: "secret",
Value: "",
Usage: "Client Secret",
},
&cli.StringFlag{
Name: "secret-file",
Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file",
},
&cli.StringFlag{
Name: "auto-discover-url",
Value: "",
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
},
&cli.StringFlag{
Name: "use-custom-urls",
Value: "false",
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-tenant-id",
Value: "",
Usage: "Use custom Tenant ID for OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-auth-url",
Value: "",
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-token-url",
Value: "",
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-profile-url",
Value: "",
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-email-url",
Value: "",
Usage: "Use a custom Email URL (option for GitHub)",
},
&cli.StringFlag{
Name: "icon-url",
Value: "",
Usage: "Custom icon URL for OAuth2 login source",
},
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Set to true to skip local 2fa for users authenticated by this source",
},
&cli.StringSliceFlag{
Name: "scopes",
Value: nil,
Usage: "Scopes to request when to authenticate against this OAuth2 source",
},
&cli.StringFlag{
Name: "required-claim-name",
Value: "",
Usage: "Claim name that has to be set to allow users to login with this source",
},
&cli.StringFlag{
Name: "required-claim-value",
Value: "",
Usage: "Claim value that has to be set to allow users to login with this source",
},
&cli.StringSliceFlag{
Name: "required-claim-values",
Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)",
},
&cli.StringFlag{
Name: "group-claim-name",
Value: "",
Usage: "Claim name providing group names for this source",
},
&cli.StringFlag{
Name: "admin-group",
Value: "",
Usage: "Group Claim value for administrator users",
},
&cli.StringFlag{
Name: "restricted-group",
Value: "",
Usage: "Group Claim value for restricted users",
},
&cli.StringFlag{
Name: "group-team-map",
Value: "",
Usage: "JSON mapping between groups and org teams",
},
&cli.BoolFlag{
Name: "group-team-map-removal",
Usage: "Activate automatic team membership removal depending on groups",
},
}
microcmdAuthAddOauth = &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
}
microcmdAuthUpdateOauth = &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
}
)
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
TokenURL: c.String("custom-token-url"),
AuthURL: c.String("custom-auth-url"),
ProfileURL: c.String("custom-profile-url"),
EmailURL: c.String("custom-email-url"),
Tenant: c.String("custom-tenant-id"),
}
} else {
customURLMapping = nil
}
return &oauth2.Source{
Provider: c.String("provider"),
ClientID: c.String("key"),
ClientSecret: c.String("secret"),
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"),
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
RequiredClaimValues: c.StringSlice("required-claim-values"),
GroupClaimName: c.String("group-claim-name"),
AdminGroup: c.String("admin-group"),
RestrictedGroup: c.String("restricted-group"),
GroupTeamMap: c.String("group-team-map"),
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
}
}
func runAddOauth(c *cli.Context) error {
secret, selected, err := oauthSecretInput(c)
if err != nil {
return err
}
ctx, cancel := installSignals()
defer cancel()
ctx = db.WithSensitiveSQL(ctx)
if err := initDB(ctx); err != nil {
return err
}
if db.SQLLoggingEnabled(ctx) {
return errors.New("OAuth configuration requires SQL logging disabled")
}
config := parseOAuth2Config(c)
if selected {
config.ClientSecret = secret
}
if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil {
return err
}
if config.RequiredClaimValues != nil && config.Provider != "openidConnect" {
return errors.New("required-claim-values requires openidConnect")
}
if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
}
}
err = auth_model.CreateSource(ctx, &auth_model.Source{
Type: auth_model.OAuth2,
Name: c.String("name"),
IsActive: true,
Cfg: config,
})
if err != nil && c.IsSet("secret-file") {
return errors.New("OAuth source creation failed")
}
return err
}
func runUpdateOauth(c *cli.Context) error {
secret, selected, err := oauthSecretInput(c)
if err != nil {
return err
}
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
defer cancel()
ctx = db.WithSensitiveSQL(ctx)
if err := initDB(ctx); err != nil {
return err
}
if db.SQLLoggingEnabled(ctx) {
return errors.New("OAuth configuration requires SQL logging disabled")
}
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
oAuth2Config, ok := source.Cfg.(*oauth2.Source)
if !ok {
return errors.New("authentication source is not OAuth2")
}
if c.IsSet("name") {
source.Name = c.String("name")
}
if c.IsSet("provider") {
oAuth2Config.Provider = c.String("provider")
}
if c.IsSet("key") {
oAuth2Config.ClientID = c.String("key")
}
if selected {
oAuth2Config.ClientSecret = secret
}
if c.IsSet("auto-discover-url") {
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
}
if c.IsSet("icon-url") {
oAuth2Config.IconURL = c.String("icon-url")
}
if c.IsSet("scopes") {
oAuth2Config.Scopes = c.StringSlice("scopes")
}
if c.IsSet("required-claim-name") {
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
}
if c.IsSet("required-claim-value") {
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
}
if c.IsSet("required-claim-values") {
oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values")
}
if c.IsSet("group-claim-name") {
oAuth2Config.GroupClaimName = c.String("group-claim-name")
}
if c.IsSet("admin-group") {
oAuth2Config.AdminGroup = c.String("admin-group")
}
if c.IsSet("restricted-group") {
oAuth2Config.RestrictedGroup = c.String("restricted-group")
}
if c.IsSet("group-team-map") {
oAuth2Config.GroupTeamMap = c.String("group-team-map")
}
if c.IsSet("group-team-map-removal") {
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
}
// update custom URL mapping
customURLMapping := &oauth2.CustomURLMapping{}
if oAuth2Config.CustomURLMapping != nil {
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
customURLMapping.TokenURL = c.String("custom-token-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
customURLMapping.AuthURL = c.String("custom-auth-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
customURLMapping.ProfileURL = c.String("custom-profile-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
customURLMapping.EmailURL = c.String("custom-email-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
customURLMapping.Tenant = c.String("custom-tenant-id")
}
oAuth2Config.CustomURLMapping = customURLMapping
if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil {
return err
}
if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" {
return errors.New("required-claim-values requires openidConnect")
}
source.Cfg = oAuth2Config
err = auth_model.UpdateSource(ctx, source)
if err != nil && c.IsSet("secret-file") {
return errors.New("OAuth source update failed")
}
return err
}
// Resolve before opening the DB or mutating a loaded source. Never store the
// resolved bytes in the CLI context: they must not become flag/argv diagnostics.
func oauthSecretInput(c *cli.Context) (string, bool, error) {
if c.IsSet("secret-file") {
if c.IsSet("secret") {
return "", false, errors.New("secret and secret-file are mutually exclusive")
}
value, err := readOAuthSecretFile(c.String("secret-file"))
return value, true, err
}
return c.String("secret"), c.IsSet("secret"), nil
}

View file

@ -0,0 +1,261 @@
module forgejo.org
go 1.24
toolchain go1.24.3
require (
code.forgejo.org/f3/gof3/v3 v3.10.6
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
code.forgejo.org/forgejo/levelqueue v1.0.0
code.forgejo.org/forgejo/reply v1.0.2
code.forgejo.org/go-chi/binding v1.0.0
code.forgejo.org/go-chi/cache v1.0.0
code.forgejo.org/go-chi/captcha v1.0.1
code.forgejo.org/go-chi/session v1.0.1
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/sdk/gitea v0.20.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.17.0
github.com/42wim/httpsig v1.2.2
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.1.6
github.com/PuerkitoBio/goquery v1.10.2
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/alecthomas/chroma/v2 v2.15.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.2
github.com/buildkite/terminal-to-html/v3 v3.16.8
github.com/caddyserver/certmagic v0.22.2
github.com/chi-middleware/proxy v1.1.1
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
github.com/dustin/go-humanize v1.0.1
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/emersion/go-imap v1.2.1
github.com/felixge/fgprof v0.9.5
github.com/fsnotify/fsnotify v1.8.0
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.0
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.20.14
github.com/go-sql-driver/mysql v1.9.1
github.com/go-webauthn/webauthn v0.12.2
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-github/v64 v64.0.0
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime/v2 v2.1.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.17.11
github.com/klauspost/cpuid/v2 v2.2.10
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.80.0
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-sqlite3 v1.14.28
github.com/meilisearch/meilisearch-go v0.31.0
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27
github.com/minio/minio-go/v7 v7.0.88
github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.7.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.7.3
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.6
github.com/valyala/fastjson v1.6.4
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
gitlab.com/gitlab-org/api/client-go v0.126.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.36.0
golang.org/x/image v0.25.0
golang.org/x/net v0.38.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sync v0.12.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
google.golang.org/protobuf v1.36.4
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.5.0
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
)
require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/geo v0.2.3 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/swag v0.22.7 // indirect
github.com/go-webauthn/x v0.1.20 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/libdns/libdns v0.2.3 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v3 v3.1.1 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rhysd/actionlint v1.6.27 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.31.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.25.1
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1
replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616
replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078
require github.com/netkingdom/access-engine v0.0.0
replace github.com/netkingdom/access-engine => ./third_party/access-engine-pep

View file

@ -0,0 +1,47 @@
// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package db
import (
"context"
"forgejo.org/modules/setting"
"xorm.io/xorm"
)
type sensitiveSQLKey struct{}
// WithSensitiveSQL suppresses argument, statement and driver-error disclosure
// in the application's query hooks. The caller must separately refuse raw XORM
// SQL logging; custom driver/server auditing remains an owner custody concern.
func WithSensitiveSQL(ctx context.Context) context.Context {
return context.WithValue(ctx, sensitiveSQLKey{}, true)
}
func isSensitiveSQL(ctx context.Context) bool {
sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool)
return sensitive
}
// SQLLoggingEnabled checks the underlying engine without changing the context or
// assuming that a sensitive context still exposes an Engine rather than Session.
// Unknown/uninitialized backends refuse, and query redaction never permits raw
// XORM logging. It neither creates a transaction nor closes an existing one.
func SQLLoggingEnabled(ctx context.Context) bool {
if setting.Database.LogSQL {
return true
}
engine := getEngine(ctx)
if engine == nil {
if x == nil {
return true
}
engine = x
}
switch current := engine.(type) {
case *xorm.Engine:
return current == nil || current.Logger() == nil || current.Logger().IsShowSQL()
case *xorm.Session:
return current == nil || current.Engine() == nil || current.Engine().Logger() == nil || current.Engine().Logger().IsShowSQL()
default:
return true
}
}

View file

@ -0,0 +1,41 @@
package db
import (
"context"
"testing"
"forgejo.org/modules/setting"
"xorm.io/xorm"
)
func TestSensitiveSQLLoggingChecksUnderlyingEngine(t *testing.T) {
engine, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
t.Fatal(err)
}
defer engine.Close()
engine.SetLogger(NewXORMLogger(false))
session := engine.NewSession()
defer session.Close()
previous := setting.Database.LogSQL
defer func() { setting.Database.LogSQL = previous }()
for name, backend := range map[string]Engine{"engine": engine, "session": session} {
t.Run(name, func(t *testing.T) {
ctx := WithSensitiveSQL(newContext(context.Background(), backend, false))
setting.Database.LogSQL = false
engine.ShowSQL(false)
if SQLLoggingEnabled(ctx) {
t.Fatal("private context with disabled actual SQL logger refused")
}
engine.ShowSQL(true)
if !SQLLoggingEnabled(ctx) {
t.Fatal("actual raw SQL logger escaped refusal")
}
engine.ShowSQL(false)
setting.Database.LogSQL = true
if !SQLLoggingEnabled(ctx) {
t.Fatal("configured raw SQL logger escaped refusal")
}
})
}
}

View file

@ -0,0 +1,205 @@
// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package oauth2
import (
"context"
"crypto"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/markbates/goth"
"github.com/netkingdom/access-engine/pkg/api"
"github.com/netkingdom/access-engine/pkg/decisionpep"
)
var errApplicationDecision = errors.New("current authenticated application access refused")
// AccessEngineAdmission contains owner-admitted public trust, with only a file
// reference to the private PDP caller token. It is not persisted OAuth source
// configuration and cannot be replaced by token claims or provider discovery.
type AccessEngineAdmission struct {
SourceName string `json:"source_name"`
Issuer string `json:"issuer"`
Audience string `json:"audience"`
TokenType string `json:"token_type"`
Algorithms []string `json:"algorithms"`
IdentityKeys map[string]string `json:"identity_keys"`
TenantClaim string `json:"tenant_claim"`
PrincipalTypeClaim string `json:"principal_type_claim"`
AssuranceClaim string `json:"assurance_claim"`
MaxIdentityAge string `json:"max_identity_age"`
Decision decisionpep.Admission `json:"decision"`
}
// VerifiedApplicationIdentity is derived only by VerifyApplicationIdentity from
// independently admitted JOSE keys. RawData and UserInfo never supply these
// fields. This snapshot is an identity fact, never an authorization verdict.
type VerifiedApplicationIdentity struct {
Issuer string
Subject string
Tenant string
PrincipalType api.SubjectType
Assurance string
NotAfter time.Time
}
func loadAccessEngineAdmission(path string) (AccessEngineAdmission, error) {
var a AccessEngineAdmission
if !filepath.IsAbs(path) {
return a, errApplicationDecision
}
f, e := os.Open(path)
if e != nil {
return a, errApplicationDecision
}
defer f.Close()
stat, e := f.Stat()
if e != nil || !stat.Mode().IsRegular() || stat.Mode().Perm()&0022 != 0 {
return a, errApplicationDecision
}
raw, e := io.ReadAll(io.LimitReader(f, 1<<20+1))
if e != nil || len(raw) > 1<<20 {
return a, errApplicationDecision
}
d := json.NewDecoder(strings.NewReader(string(raw)))
d.DisallowUnknownFields()
if d.Decode(&a) != nil {
return a, errApplicationDecision
}
if _, e = d.Token(); e != io.EOF {
return a, errApplicationDecision
}
return a, nil
}
// VerifyApplicationIdentity validates the ID token independently of Goth's
// decode/claim checks and of the UserInfo merge. Keys and accepted algorithm,
// issuer, audience and type are admitted separately; none come from the JWT URL.
func VerifyApplicationIdentity(raw string, a AccessEngineAdmission, now time.Time) (VerifiedApplicationIdentity, error) {
var out VerifiedApplicationIdentity
age, e := time.ParseDuration(a.MaxIdentityAge)
if e != nil || age <= 0 || a.Issuer == "" || a.Audience == "" || a.TokenType == "" || len(a.Algorithms) == 0 || len(a.IdentityKeys) == 0 || a.TenantClaim == "" || a.PrincipalTypeClaim == "" || a.AssuranceClaim == "" {
return out, errApplicationDecision
}
for _, alg := range a.Algorithms {
if alg != "RS256" && alg != "RS384" && alg != "RS512" && alg != "ES256" && alg != "ES384" && alg != "ES512" && alg != "EdDSA" {
return out, errApplicationDecision
}
}
claims := jwt.MapClaims{}
token, e := jwt.ParseWithClaims(raw, claims, func(t *jwt.Token) (any, error) {
if typ, ok := t.Header["typ"].(string); !ok || typ != a.TokenType {
return nil, errApplicationDecision
}
kid, ok := t.Header["kid"].(string)
if !ok || kid == "" {
return nil, errApplicationDecision
}
text, ok := a.IdentityKeys[kid]
if !ok {
return nil, errApplicationDecision
}
block, rest := pem.Decode([]byte(text))
if block == nil || block.Type != "PUBLIC KEY" || len(strings.TrimSpace(string(rest))) != 0 {
return nil, errApplicationDecision
}
key, e := x509.ParsePKIXPublicKey(block.Bytes)
if e != nil {
return nil, errApplicationDecision
}
var pub crypto.PublicKey = key
return pub, nil
}, jwt.WithValidMethods(a.Algorithms), jwt.WithIssuer(a.Issuer), jwt.WithAudience(a.Audience), jwt.WithExpirationRequired(), jwt.WithIssuedAt(), jwt.WithJSONNumber(), jwt.WithTimeFunc(func() time.Time { return now }))
if e != nil || token == nil || !token.Valid {
return out, errApplicationDecision
}
subject, e := claims.GetSubject()
if e != nil || subject == "" {
return out, errApplicationDecision
}
issued, e := claims.GetIssuedAt()
if e != nil || issued == nil || issued.Time.After(now) || now.Sub(issued.Time) > age {
return out, errApplicationDecision
}
end, e := claims.GetExpirationTime()
if e != nil || end == nil || !now.Before(end.Time) {
return out, errApplicationDecision
}
aud, e := claims.GetAudience()
if e != nil {
return out, errApplicationDecision
}
if authorizedParty, present := claims["azp"]; present {
if azp, ok := authorizedParty.(string); !ok || azp != a.Audience {
return out, errApplicationDecision
}
} else if len(aud) > 1 {
return out, errApplicationDecision
}
value := func(name string) (string, bool) { v, ok := claims[name].(string); return v, ok && validClaimLiteral(v) }
tenant, ok := value(a.TenantClaim)
if !ok {
return out, errApplicationDecision
}
principal, ok := value(a.PrincipalTypeClaim)
if !ok {
return out, errApplicationDecision
}
assurance, ok := value(a.AssuranceClaim)
if !ok {
return out, errApplicationDecision
}
return VerifiedApplicationIdentity{Issuer: a.Issuer, Subject: subject, Tenant: tenant, PrincipalType: api.SubjectType(principal), Assurance: assurance, NotAfter: end.Time}, nil
}
func (source *Source) authorizeAccessEngine(ctx context.Context, user goth.User) error {
// The guarded target extension cannot silently fall back to a local group
// decision when admission, keys, facts, PDP or caller authentication is absent.
if source.Provider != "openidConnect" || ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues) != nil {
return errApplicationDecision
}
a, e := loadAccessEngineAdmission(os.Getenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE"))
if e != nil || source.authSource == nil || a.SourceName != source.authSource.Name || a.Audience != source.ClientID {
return errApplicationDecision
}
identity, e := VerifyApplicationIdentity(user.IDToken, a, time.Now().UTC())
if e != nil {
return errApplicationDecision
}
// RequireCurrentUserInfoClaim has refused missing/malformed current groups
// before Goth can merge. The validated ID subject must still match that join.
sub, ok := user.RawData["sub"].(string)
if !ok || sub != identity.Subject || user.UserID != identity.Subject {
return errApplicationDecision
}
groups, ok := strictClaimValues(user.RawData[source.RequiredClaimName])
if !ok {
return errApplicationDecision
}
client, e := decisionpep.New(a.Decision)
if e != nil {
return errApplicationDecision
}
request := api.CheckRequest{Tenant: identity.Tenant,
Subject: api.SubjectRef{ID: identity.Subject, Type: identity.PrincipalType, Tenant: identity.Tenant,
Attributes: map[string]any{"issuer": identity.Issuer, "assurance": identity.Assurance, "current_userinfo_groups": groups}},
Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: identity.Tenant},
Context: map[string]any{"identity_issuer": identity.Issuer, "identity_subject": identity.Subject, "identity_tenant": identity.Tenant, "identity_principal_type": string(identity.PrincipalType), "identity_assurance": identity.Assurance, "identity_not_after": identity.NotAfter.UTC().Format(time.RFC3339Nano), "current_userinfo_groups": groups, "required_groups_any_of": source.RequiredClaimValues, "source_name": a.SourceName, "source_id": source.authSource.ID}}
if _, e = client.Check(ctx, request); e != nil {
return errApplicationDecision
}
if !time.Now().UTC().Before(identity.NotAfter) {
return errApplicationDecision
}
return nil
}

View file

@ -0,0 +1,34 @@
package oauth2
import "testing"
func TestAccessEngineAuthorizedPartyRefusesAnotherClient(t *testing.T) {
for name, value := range map[string]any{
"other-client": "other-client",
"number": 17,
"array": []string{"forgejo"},
"empty": "",
} {
t.Run(name, func(t *testing.T) {
admission, key, claims, now := identityFixture(t)
claims["azp"] = value
token := signedIdentity(t, key, claims, nil)
if _, err := VerifyApplicationIdentity(token, admission, now); err == nil {
t.Fatal("a valid signature and audience concealed an invalid authorized party")
}
})
}
}
func TestAccessEngineAuthorizedPartyAcceptsAdmittedClient(t *testing.T) {
for _, multiple := range []bool{false, true} {
admission, key, claims, now := identityFixture(t)
claims["azp"] = admission.Audience
if multiple {
claims["aud"] = []string{admission.Audience, "another-resource"}
}
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err != nil {
t.Fatal("admitted authorized party refused", err)
}
}
}

View file

@ -0,0 +1,180 @@
package oauth2
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/json"
"encoding/pem"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"time"
authmodel "forgejo.org/models/auth"
"github.com/golang-jwt/jwt/v5"
"github.com/markbates/goth"
"github.com/markbates/goth/gothic"
"github.com/netkingdom/access-engine/pkg/api"
"github.com/netkingdom/access-engine/pkg/decisionpep"
"github.com/netkingdom/access-engine/testsupport/signfixture"
)
// This fixture substitutes only Goth's completed authentication result. The
// actual Source.Callback, JOSE verifier, HTTPS PEP client and producer signature
// implementation execute. Actual issuer login and native admission are separate.
func callbackFixture(t *testing.T, group, mode string) (*Source, *goth.User, *atomic.Int64) {
t.Helper()
admission, identityKey, claims, _ := identityFixture(t)
identityToken := signedIdentity(t, identityKey, claims, nil)
pub, key, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
calls := new(atomic.Int64)
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls.Add(1)
if r.URL.Path != "/v1/check" || r.Method != http.MethodPost || r.Header.Get("Authorization") != "Bearer synthetic-caller" || r.Header.Get("Access-Engine-JSON-Profile") != decisionpep.JSONProfile {
t.Error("incorrect authenticated PDP request")
w.WriteHeader(403)
return
}
var request api.CheckRequest
decoder := json.NewDecoder(r.Body)
decoder.UseNumber()
if err := decoder.Decode(&request); err != nil {
t.Error(err)
w.WriteHeader(400)
return
}
c := request.Context
if request.Subject.ID != "user:synthetic" || request.Subject.Tenant != "tenant:synthetic" || request.Subject.Type != "human" || request.Action != "application.access" || request.Resource.ID != "application:forgejo" || c["identity_issuer"] != "https://issuer.invalid" || c["identity_assurance"] != "aal2" || c["identity_tenant"] != "tenant:synthetic" || c["source_id"] != json.Number("17") {
t.Error("PDP request did not use authenticated identity and source")
w.WriteHeader(400)
return
}
now := time.Now().UTC()
envelope := api.DecisionEnvelope{ID: "synthetic-decision", RequestID: request.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: "v1", Subject: request.Subject, Resource: request.Resource, Binding: api.NewDecisionBindingFor(request, request), Provenance: api.DecisionProvenance{Evaluator: "flex-auth/local", Mode: "standalone/" + decisionpep.JSONProfile, PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyPackageDigest: "sha256:" + strings.Repeat("1", 64), RegistrySnapshotDigest: "sha256:" + strings.Repeat("2", 64), Caller: &api.CallerProvenance{Mode: "enforce", Principal: "system:serviceaccount:synthetic:forgejo", Audience: "access-engine", NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}}
api.CompleteDecision(&envelope, request, api.DecisionCompletion{AllowTTL: "10s", Now: now})
switch mode {
case "deny":
envelope.Effect = api.DecisionEffectDeny
case "wrong-binding":
envelope.Binding.Subject.ID = "other"
case "wrong-pin":
envelope.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64)
case "expired":
envelope.Lifetime.ExpiresAt = now.Add(-time.Hour).Format(time.RFC3339Nano)
}
if mode != "unsigned" {
signingKey := key
if mode == "wrong-key" {
_, signingKey, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Error(err)
return
}
}
if err := signfixture.Sign(&envelope, signingKey, "synthetic-pdp"); err != nil {
t.Error(err)
return
}
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(envelope); err != nil {
t.Error(err)
}
}))
t.Cleanup(server.Close)
dir := t.TempDir()
ca := filepath.Join(dir, "ca.pem")
token := filepath.Join(dir, "caller")
config := filepath.Join(dir, "admission.json")
if err := os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0600); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(token, []byte("synthetic-caller"), 0600); err != nil {
t.Fatal(err)
}
admission.SourceName = "synthetic-source"
admission.Decision = decisionpep.Admission{Endpoint: server.URL + "/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"synthetic-pdp": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:synthetic:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "2s"}
raw, err := json.Marshal(admission)
if err != nil {
t.Fatal(err)
}
if err = os.WriteFile(config, raw, 0600); err != nil {
t.Fatal(err)
}
t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", config)
source := &Source{Provider: "openidConnect", ClientID: admission.Audience, RequiredClaimName: "groups", RequiredClaimValues: []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}}
source.authSource = &authmodel.Source{ID: 17, Name: admission.SourceName}
user := &goth.User{UserID: "user:synthetic", IDToken: identityToken, RawData: map[string]any{"sub": "user:synthetic", "groups": []string{group}}}
previous := gothic.CompleteUserAuth
gothic.CompleteUserAuth = func(http.ResponseWriter, *http.Request) (goth.User, error) { return *user, nil }
t.Cleanup(func() { gothic.CompleteUserAuth = previous })
return source, user, calls
}
func TestAccessEngineActualCallbackAllowsAuthenticatedDecision(t *testing.T) {
for _, group := range []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} {
t.Run(group, func(t *testing.T) {
source, user, calls := callbackFixture(t, group, "allow")
// Mutable UserInfo cannot supply identity assurance, issuer or tenant.
user.RawData["acr"] = "aal1"
user.RawData["tenant"] = "tenant:attacker"
user.RawData["iss"] = "https://attacker.invalid"
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
if err != nil || got.UserID != user.UserID || calls.Load() != 1 {
t.Fatalf("callback result: err=%v requests=%d", err, calls.Load())
}
})
}
}
func TestAccessEngineActualCallbackRefusesUnverifiedPDP(t *testing.T) {
for _, mode := range []string{"deny", "unsigned", "wrong-key", "wrong-pin", "wrong-binding", "expired"} {
t.Run(mode, func(t *testing.T) {
source, _, calls := callbackFixture(t, "forge-admins", mode)
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
if err == nil || got.UserID != "" || got.IDToken != "" || calls.Load() != 1 {
t.Fatalf("unverified decision escaped callback: requests=%d err=%v", calls.Load(), err)
}
})
}
}
func TestAccessEngineActualCallbackChecksIdentityBeforePDP(t *testing.T) {
cases := map[string]func(*Source, *goth.User){
"missing-token": func(_ *Source, u *goth.User) { u.IDToken = "" },
"mismatched-user": func(_ *Source, u *goth.User) { u.UserID = "other" },
"mismatched-subject": func(_ *Source, u *goth.User) { u.RawData["sub"] = "other" },
"missing-groups": func(_ *Source, u *goth.User) { delete(u.RawData, "groups") },
"wrong-source": func(s *Source, _ *goth.User) { s.authSource.Name = "other" },
"wrong-provider": func(s *Source, _ *goth.User) { s.Provider = "other" },
"wrong-audience": func(s *Source, _ *goth.User) { s.ClientID = "other" },
"missing-admission": func(_ *Source, _ *goth.User) { t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", "") },
"expired-token": func(_ *Source, u *goth.User) {
_, key, claims, now := identityFixture(t)
claims["exp"] = now.Add(-time.Hour).Unix()
u.IDToken = signedIdentity(t, key, claims, nil)
},
"untrusted-identity": func(_ *Source, u *goth.User) {
token := jwt.NewWithClaims(jwt.SigningMethodNone, jwt.MapClaims{"sub": "user:synthetic"})
u.IDToken, _ = token.SignedString(jwt.UnsafeAllowNoneSignatureType)
},
}
for name, mutate := range cases {
t.Run(name, func(t *testing.T) {
source, user, calls := callbackFixture(t, "forge-admins", "allow")
mutate(source, user)
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
if err == nil || got.UserID != "" || calls.Load() != 0 {
t.Fatalf("unverified identity reached PDP: requests=%d err=%v", calls.Load(), err)
}
})
}
}

View file

@ -0,0 +1,100 @@
package oauth2
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
)
func identityFixture(t *testing.T) (AccessEngineAdmission, ed25519.PrivateKey, jwt.MapClaims, time.Time) {
t.Helper()
pub, key, e := ed25519.GenerateKey(rand.Reader)
if e != nil {
t.Fatal(e)
}
der, e := x509.MarshalPKIXPublicKey(pub)
if e != nil {
t.Fatal(e)
}
now := time.Now().UTC().Truncate(time.Second)
a := AccessEngineAdmission{Issuer: "https://issuer.invalid", Audience: "forgejo", TokenType: "JWT", Algorithms: []string{"EdDSA"}, IdentityKeys: map[string]string{"synthetic": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))}, TenantClaim: "tenant", PrincipalTypeClaim: "principal_type", AssuranceClaim: "acr", MaxIdentityAge: "5m"}
c := jwt.MapClaims{"iss": a.Issuer, "aud": a.Audience, "sub": "user:synthetic", "iat": now.Add(-time.Second).Unix(), "exp": now.Add(time.Minute).Unix(), "tenant": "tenant:synthetic", "principal_type": "human", "acr": "aal2"}
return a, key, c, now
}
func signedIdentity(t *testing.T, key ed25519.PrivateKey, c jwt.MapClaims, mutate func(*jwt.Token)) string {
t.Helper()
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c)
token.Header["kid"] = "synthetic"
token.Header["typ"] = "JWT"
if mutate != nil {
mutate(token)
}
raw, e := token.SignedString(key)
if e != nil {
t.Fatal(e)
}
return raw
}
func TestVerifiedApplicationIdentityActualJOSE(t *testing.T) {
a, key, c, now := identityFixture(t)
raw := signedIdentity(t, key, c, nil)
got, e := VerifyApplicationIdentity(raw, a, now)
if e != nil {
t.Fatal(e)
}
if got.Subject != "user:synthetic" || got.Tenant != "tenant:synthetic" || got.Assurance != "aal2" || got.Issuer != a.Issuer || string(got.PrincipalType) != "human" {
t.Fatal("identity did not come from verified token")
}
cases := map[string]func(jwt.MapClaims){"issuer": func(c jwt.MapClaims) { c["iss"] = "https://other.invalid" }, "audience": func(c jwt.MapClaims) { c["aud"] = "other" }, "expired": func(c jwt.MapClaims) { c["exp"] = now.Add(-time.Nanosecond).Unix() }, "future-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(time.Minute).Unix() }, "old-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(-10 * time.Minute).Unix() }, "no-exp": func(c jwt.MapClaims) { delete(c, "exp") }, "no-iat": func(c jwt.MapClaims) { delete(c, "iat") }, "no-tenant": func(c jwt.MapClaims) { delete(c, "tenant") }, "no-principal": func(c jwt.MapClaims) { delete(c, "principal_type") }, "no-assurance": func(c jwt.MapClaims) { delete(c, "acr") }, "multi-audience-no-azp": func(c jwt.MapClaims) { c["aud"] = []string{"forgejo", "other"} }, "empty-subject": func(c jwt.MapClaims) { c["sub"] = "" }}
for name, change := range cases {
t.Run(name, func(t *testing.T) {
copy := jwt.MapClaims{}
for k, v := range c {
copy[k] = v
}
change(copy)
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, copy, nil), a, now); e == nil {
t.Fatal("unverified identity accepted")
}
})
}
t.Run("wrong-key", func(t *testing.T) {
_, other, _ := ed25519.GenerateKey(rand.Reader)
if _, e := VerifyApplicationIdentity(signedIdentity(t, other, c, nil), a, now); e == nil {
t.Fatal("wrong key accepted")
}
})
t.Run("unknown-key", func(t *testing.T) {
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["kid"] = "other" }), a, now); e == nil {
t.Fatal("unknown key accepted")
}
})
t.Run("wrong-type", func(t *testing.T) {
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["typ"] = "at+jwt" }), a, now); e == nil {
t.Fatal("access token accepted as ID token")
}
})
t.Run("algorithm-confusion", func(t *testing.T) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
token.Header["kid"] = "synthetic"
token.Header["typ"] = "JWT"
raw, _ := token.SignedString([]byte(a.IdentityKeys["synthetic"]))
if _, e := VerifyApplicationIdentity(raw, a, now); e == nil {
t.Fatal("algorithm confusion accepted")
}
})
t.Run("none", func(t *testing.T) {
token := jwt.NewWithClaims(jwt.SigningMethodNone, c)
token.Header["kid"] = "synthetic"
token.Header["typ"] = "JWT"
raw, _ := token.SignedString(jwt.UnsafeAllowNoneSignatureType)
if _, e := VerifyApplicationIdentity(raw, a, now); e == nil {
t.Fatal("unsigned identity accepted")
}
})
}

View file

@ -0,0 +1,74 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package oauth2
import (
"net/http"
"net/url"
"github.com/markbates/goth"
"github.com/markbates/goth/gothic"
)
// Callout redirects request/response pair to authenticate against the provider
func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error {
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
var querySuffix string
if codeChallengeS256 != "" {
querySuffix = "&" + url.Values{
"code_challenge_method": []string{"S256"},
"code_challenge": []string{codeChallengeS256},
}.Encode()
}
// don't use the default gothic begin handler to prevent issues when some error occurs
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
// gothic.BeginAuthHandler(response, request)
gothRWMutex.RLock()
defer gothRWMutex.RUnlock()
url, err := gothic.GetAuthURL(response, request)
if err == nil {
// hacky way to set the code_challenge, but no better way until
// https://github.com/markbates/goth/issues/516 is resolved
http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect)
}
return err
}
// Callback handles OAuth callback, resolve to a goth user and send back to original url
// this will trigger a new authentication request, but because we save it in the session we can use that
func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) {
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
if codeVerifier != "" {
// hacky way to set the code_verifier...
// Will be picked up inside CompleteUserAuth: params := req.URL.Query()
// https://github.com/markbates/goth/pull/474/files
request = request.Clone(request.Context())
q := request.URL.Query()
q.Add("code_verifier", codeVerifier)
request.URL.RawQuery = q.Encode()
}
gothRWMutex.RLock()
defer gothRWMutex.RUnlock()
user, err := gothic.CompleteUserAuth(response, request)
if err != nil {
return user, err
}
if source.RequiredClaimValues != nil {
if err := source.authorizeAccessEngine(request.Context(), user); err != nil {
return goth.User{}, err
}
}
return user, nil
}

View file

@ -0,0 +1,80 @@
BIN_DIR ?= bin
BIN := $(BIN_DIR)/access-engine
PKG := ./...
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo 0.0.0-dev)
LDFLAGS := -X main.version=$(VERSION)
.PHONY: all build test vet lint fmt tidy sbom clean ci overlay-render overlay-dry-run verify-posture check-layer
all: vet lint test build
build:
@mkdir -p $(BIN_DIR)
go build -ldflags "$(LDFLAGS)" -o $(BIN) ./cmd/access-engine
test: check-layer
go test -race $(PKG)
check-layer:
go run ./tools/check_layer_conformance.go
vet:
go vet $(PKG)
fmt:
gofmt -l -w .
tidy:
go mod tidy
lint:
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run $(PKG); \
else \
echo "golangci-lint not installed; run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
echo "falling back to: go vet"; \
go vet $(PKG); \
fi
GOBIN_PATH := $(shell go env GOPATH)/bin
sbom:
@mkdir -p $(BIN_DIR)
@if command -v cyclonedx-gomod >/dev/null 2>&1; then \
cyclonedx-gomod mod -json -output $(BIN_DIR)/sbom.cdx.json .; \
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (cyclonedx-gomod)"; \
elif [ -x "$(GOBIN_PATH)/cyclonedx-gomod" ]; then \
"$(GOBIN_PATH)/cyclonedx-gomod" mod -json -output $(BIN_DIR)/sbom.cdx.json .; \
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (cyclonedx-gomod via GOPATH)"; \
elif command -v syft >/dev/null 2>&1; then \
syft . -o cyclonedx-json=$(BIN_DIR)/sbom.cdx.json; \
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (syft)"; \
else \
echo "no SBOM tool found. install one:"; \
echo " go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest"; \
echo " or syft: https://github.com/anchore/syft"; \
exit 1; \
fi
clean:
rm -rf $(BIN_DIR)
ci: vet lint test build overlay-render
verify-posture:
@bash tools/verify-posture.sh
.PHONY: verify-emission-cadence
verify-emission-cadence:
python3 tools/verify_emission_cadence.py
overlay-render:
@tests/stage1.sh
overlay-dry-run:
@test -n "$$KUBECONFIG" || { echo "set KUBECONFIG to the railiance01 kubeconfig"; exit 1; }
@for values in values/stage2-canary.yaml values/user-engine.yaml values/tenant-engine.yaml; do \
echo "server-dry-run $$values"; \
helm template flex-auth charts/flex-auth -f "$$values" --namespace flex-auth \
| kubectl apply --dry-run=server --server-side -f -; \
done

View file

@ -0,0 +1,757 @@
// Command flex-auth is the CLI entry point for the flex-auth authorization
// registry and control plane.
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"gopkg.in/yaml.v3"
"github.com/netkingdom/access-engine/internal/audit"
"github.com/netkingdom/access-engine/internal/callerauth"
decisioncore "github.com/netkingdom/access-engine/internal/decision"
"github.com/netkingdom/access-engine/internal/emission"
"github.com/netkingdom/access-engine/internal/policy"
"github.com/netkingdom/access-engine/internal/registry"
"github.com/netkingdom/access-engine/internal/taskexecution"
"github.com/netkingdom/access-engine/pkg/api"
)
// version is set at build time via -ldflags "-X main.version=...".
var version = "0.0.0-dev"
func main() {
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
}
func run(args []string, stdout, stderr io.Writer) int {
if len(args) == 0 {
printUsage(stderr)
return 64
}
switch args[0] {
case "version", "--version", "-version":
fmt.Fprintln(stdout, version)
return 0
case "validate":
return runValidate(args[1:], stdout, stderr)
case "load-registry":
return runLoadRegistry(args[1:], stdout, stderr)
case "test-policy":
return runTestPolicy(args[1:], stdout, stderr)
case "check":
return runCheck(args[1:], stdout, stderr)
case "batch-check":
return runBatchCheck(args[1:], stdout, stderr)
case "list-allowed":
return runListAllowed(args[1:], stdout, stderr)
case "explain":
return runExplain(args[1:], stdout, stderr)
case "serve":
return runServe(args[1:], stdout, stderr)
case "serve-readback":
return runServeReadback(args[1:], stdout, stderr)
case "help", "-h", "--help":
printUsage(stdout)
return 0
default:
fmt.Fprintf(stderr, "access-engine: unknown subcommand %q\n", args[0])
printUsage(stderr)
return 64
}
}
func runValidate(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("validate", stderr)
kind := fs.String("kind", "", "resource-manifest, subject-manifest, protected-system, relationship, access-descriptor, or policy")
file := fs.String("file", "", "YAML/JSON file to validate")
if err := fs.Parse(args); err != nil {
return 64
}
if *kind == "" || *file == "" {
fmt.Fprintln(stderr, "validate requires --kind and --file")
return 64
}
switch *kind {
case "resource-manifest":
var manifest api.ResourceManifest
if err := readYAML(*file, &manifest); err != nil {
return fail(stderr, err)
}
if err := registry.NewStore().ImportResourceManifest(manifest); err != nil {
return fail(stderr, err)
}
case "subject-manifest":
var manifest api.SubjectManifest
if err := readYAML(*file, &manifest); err != nil {
return fail(stderr, err)
}
if err := registry.NewStore().ImportSubjectManifest(manifest); err != nil {
return fail(stderr, err)
}
case "protected-system":
var manifest api.ProtectedSystemManifest
if err := readYAML(*file, &manifest); err != nil {
return fail(stderr, err)
}
if err := registry.NewStore().PutProtectedSystem(manifest); err != nil {
return fail(stderr, err)
}
case "relationship":
var fact api.RelationshipFact
if err := readYAML(*file, &fact); err != nil {
return fail(stderr, err)
}
if err := registry.NewStore().PutRelationship(fact); err != nil {
return fail(stderr, err)
}
case "access-descriptor":
var descriptor api.CaringAccessDescriptor
if err := readYAML(*file, &descriptor); err != nil {
return fail(stderr, err)
}
if err := validateDescriptor(descriptor); err != nil {
return fail(stderr, err)
}
case "policy", "policy-package":
pkg, err := policy.LoadAndValidateFile(context.Background(), *file)
if err != nil {
return fail(stderr, err)
}
if err := writeJSON(stdout, pkg.Validation); err != nil {
return fail(stderr, err)
}
if !pkg.Valid {
return 1
}
return 0
default:
fmt.Fprintf(stderr, "unsupported validate kind %q\n", *kind)
return 64
}
return writeStatus(stdout, "valid", map[string]any{"kind": *kind, "file": *file})
}
func runLoadRegistry(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("load-registry", stderr)
file := fs.String("file", "", "registry snapshot JSON file")
if err := fs.Parse(args); err != nil {
return 64
}
if *file == "" {
fmt.Fprintln(stderr, "load-registry requires --file")
return 64
}
store, err := registry.LoadFile(*file)
if err != nil {
return fail(stderr, err)
}
snapshot := store.Snapshot()
return writeStatus(stdout, "loaded", map[string]any{
"file": *file,
"systems": len(snapshot.Systems),
"resource_manifests": len(snapshot.ResourceManifests),
"subjects": len(snapshot.Subjects),
"groups": len(snapshot.Groups),
"teams": len(snapshot.Teams),
"tenants": len(snapshot.Tenants),
"relationships": len(snapshot.Relationships),
})
}
func runTestPolicy(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("test-policy", stderr)
file := fs.String("file", "", "policy package Markdown file")
if err := fs.Parse(args); err != nil {
return 64
}
if *file == "" {
fmt.Fprintln(stderr, "test-policy requires --file")
return 64
}
pkg, err := policy.LoadAndValidateFile(context.Background(), *file)
if err != nil {
return fail(stderr, err)
}
if err := writeJSON(stdout, pkg.Validation); err != nil {
return fail(stderr, err)
}
if !pkg.Valid {
return 1
}
return 0
}
func runCheck(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("check", stderr)
registryPath := fs.String("registry", "", "registry snapshot JSON file")
policyPath := fs.String("policy", "", "policy package Markdown file")
requestPath := fs.String("request", "", "check request YAML/JSON file")
logPath := fs.String("log", "", "optional JSONL decision log path")
if err := fs.Parse(args); err != nil {
return 64
}
if *registryPath == "" || *policyPath == "" || *requestPath == "" {
fmt.Fprintln(stderr, "check requires --registry, --policy, and --request")
return 64
}
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
if err != nil {
return fail(stderr, err)
}
var request api.CheckRequest
if err := readYAML(*requestPath, &request); err != nil {
return fail(stderr, err)
}
decision, err := engine.Check(context.Background(), request)
if err != nil {
return fail(stderr, err)
}
if err := writeJSON(stdout, decision); err != nil {
return fail(stderr, err)
}
return 0
}
func runBatchCheck(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("batch-check", stderr)
registryPath := fs.String("registry", "", "registry snapshot JSON file")
policyPath := fs.String("policy", "", "policy package Markdown file")
requestPath := fs.String("request", "", "batch check request YAML/JSON file")
logPath := fs.String("log", "", "optional JSONL decision log path")
if err := fs.Parse(args); err != nil {
return 64
}
if *registryPath == "" || *policyPath == "" || *requestPath == "" {
fmt.Fprintln(stderr, "batch-check requires --registry, --policy, and --request")
return 64
}
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
if err != nil {
return fail(stderr, err)
}
var request api.BatchCheckRequest
if err := readYAML(*requestPath, &request); err != nil {
return fail(stderr, err)
}
decisions, err := engine.BatchCheck(context.Background(), request)
if err != nil {
return fail(stderr, err)
}
if err := writeJSON(stdout, decisions); err != nil {
return fail(stderr, err)
}
return 0
}
func runListAllowed(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("list-allowed", stderr)
registryPath := fs.String("registry", "", "registry snapshot JSON file")
policyPath := fs.String("policy", "", "policy package Markdown file")
subject := fs.String("subject", "", "subject id")
action := fs.String("action", "", "action name")
system := fs.String("system", "", "protected system id")
resourceType := fs.String("resource-type", "", "resource type")
logPath := fs.String("log", "", "optional JSONL decision log path")
var filters keyValueFlags
fs.Var(&filters, "filter", "resource filter as key=value; may be repeated")
if err := fs.Parse(args); err != nil {
return 64
}
if *registryPath == "" || *policyPath == "" || *subject == "" || *action == "" {
fmt.Fprintln(stderr, "list-allowed requires --registry, --policy, --subject, and --action")
return 64
}
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
if err != nil {
return fail(stderr, err)
}
decisions, err := engine.ListAllowed(context.Background(), decisioncore.ListAllowedRequest{
Subject: api.SubjectRef{ID: *subject},
Action: *action,
System: *system,
ResourceType: *resourceType,
Filters: filters.Map(),
})
if err != nil {
return fail(stderr, err)
}
if err := writeJSON(stdout, decisions); err != nil {
return fail(stderr, err)
}
return 0
}
func runExplain(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("explain", stderr)
logPath := fs.String("decision-log", "", "JSONL decision log path")
decisionID := fs.String("decision-id", "", "decision id to explain")
if err := fs.Parse(args); err != nil {
return 64
}
if *logPath == "" || *decisionID == "" {
fmt.Fprintln(stderr, "explain requires --decision-log and --decision-id")
return 64
}
log := audit.NewJSONLDecisionLog(*logPath)
envelope, ok, err := log.Find(*decisionID)
if err != nil {
return fail(stderr, err)
}
if !ok {
return fail(stderr, fmt.Errorf("decision %q not found", *decisionID))
}
if err := writeJSON(stdout, decisioncore.ExplainEnvelope(envelope)); err != nil {
return fail(stderr, err)
}
return 0
}
func runServe(args []string, stdout, stderr io.Writer) int {
fs := newFlagSet("serve", stderr)
addr := fs.String("addr", "127.0.0.1:8080", "HTTP listen address")
registryPath := fs.String("registry", "", "registry snapshot JSON file")
policyPath := fs.String("policy", "", "policy package Markdown file")
logPath := fs.String("log", "", "optional JSONL decision log path")
signingKeyFile := fs.String("envelope-signing-key-file", "", "owner-mounted base64url Ed25519 seed file")
signingKID := fs.String("envelope-signing-kid", "", "independently admitted envelope signing key id")
approvalSourceFile := fs.String("approval-source-file", "", "owner fixed Approval HTTPS reader configuration file")
outboxMaxBytes := fs.Int64("outbox-max-bytes", emission.MaxOutboxBytes, "bounded outbox payload bytes, maximum67108864")
outboxDir := fs.String("outbox-dir", "", "durable emission outbox directory (FLEX-WP-0031); exclusive with --log")
emissionSource := fs.String("emission-source", "", "audit-core source name for emitted decision events, e.g. flex-auth.tenant-engine")
auditURL := fs.String("audit-url", "", "admitted audit-core /v1/events endpoint; empty disables drain and heartbeats")
auditTokenFile := fs.String("audit-token-file", "", "mounted sender token file, never a token value")
callerAuthMode := fs.String("caller-auth-mode", "disabled", "disabled, warn, or enforce")
callerAudience := fs.String("caller-audience", "flex-auth", "required caller token audience")
callerKubernetesURL := fs.String("caller-kubernetes-url", "https://kubernetes.default.svc", "Kubernetes API base URL for TokenReview")
callerReviewerTokenFile := fs.String("caller-reviewer-token-file", "/var/run/secrets/flex-auth-reviewer/token", "projected Kubernetes API credential")
callerCAFile := fs.String("caller-ca-file", "/var/run/secrets/flex-auth-reviewer/ca.crt", "Kubernetes API CA bundle")
var callerBindings keyValueFlags
fs.Var(&callerBindings, "caller-binding", "resource system=authenticated Kubernetes principal (repeatable)")
if err := fs.Parse(args); err != nil {
return 64
}
if *registryPath == "" || *policyPath == "" {
fmt.Fprintln(stderr, "serve requires --registry and --policy")
return 64
}
if *outboxDir != "" && (*logPath != "" || *emissionSource == "") {
fmt.Fprintln(stderr, "serve --outbox-dir requires --emission-source and excludes --log")
return 64
}
delivery := emission.DeliveryConfig{URL: *auditURL, TokenFile: *auditTokenFile}
if *auditURL != "" || *auditTokenFile != "" {
if *outboxDir == "" {
fmt.Fprintln(stderr, "audit delivery requires --outbox-dir")
return 64
}
if err := delivery.Validate(); err != nil {
fmt.Fprintln(stderr, err)
return 64
}
}
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
if err != nil {
return fail(stderr, err)
}
if err := configureEnvelopeSigning(engine, *signingKeyFile, *signingKID); err != nil {
return fail(stderr, err)
}
var outbox *emission.Outbox
if *outboxDir != "" {
outbox, err = emission.OpenBounded(*outboxDir, *emissionSource, *outboxMaxBytes)
if err != nil {
return fail(stderr, err)
}
defer outbox.Close()
engine.SetDecisionLog(outbox)
}
authenticator, err := buildCallerAuthenticator(
callerauth.Mode(*callerAuthMode),
*callerAudience,
*callerKubernetesURL,
*callerReviewerTokenFile,
*callerCAFile,
callerBindings.StringMap(),
stderr,
)
if err != nil {
return fail(stderr, err)
}
serviceInputs, err := buildServicePilotInputs(engine, callerauth.Mode(*callerAuthMode), *callerAudience, *callerKubernetesURL, *callerReviewerTokenFile, *callerCAFile, callerBindings.StringMap())
if err != nil {
return fail(stderr, err)
}
var approvalReader *taskexecution.ApprovalReader
if engine.IsTrustedServicePilot() {
if *signingKeyFile == "" || *approvalSourceFile == "" {
return fail(stderr, fmt.Errorf("trusted task source requires signing and fixed Approval reader configuration"))
}
approvalReader, err = taskexecution.LoadApprovalReader(*approvalSourceFile)
if err != nil {
return fail(stderr, err)
}
} else if *approvalSourceFile != "" {
return fail(stderr, fmt.Errorf("Approval reader requires exact trusted task source package"))
}
mux := newServeMuxWithTaskFacts(engine, authenticator, serviceInputs, approvalReader)
if outbox != nil {
mountEmission(mux, outbox)
}
if *auditURL != "" {
ctx, cancel := context.WithCancel(context.Background())
done := make(chan struct{})
go func() {
defer close(done)
runEmissionDelivery(ctx, outbox, delivery, stderr)
}()
defer func() { cancel(); <-done }()
}
fmt.Fprintf(stderr, "access-engine serving on http://%s\n", *addr)
if err := http.ListenAndServe(*addr, mux); err != nil {
return fail(stderr, err)
}
return 0
}
func runEmissionDelivery(ctx context.Context, outbox *emission.Outbox, config emission.DeliveryConfig, stderr io.Writer) {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
if ctx.Err() != nil {
return
}
if err := outbox.AppendDueHeartbeats(); err != nil {
fmt.Fprintf(stderr, "emission heartbeat commit: %v\n", err)
}
n, err := outbox.Drain(ctx, config, 100)
if err != nil && ctx.Err() == nil {
fmt.Fprintf(stderr, "emission drain: %v\n", err)
}
if err == nil && n == 100 {
continue // Drain the next bounded batch without imposing a rate cap.
}
select {
case <-ctx.Done():
return
case <-ticker.C:
}
}
}
func newServeMux(engine *decisioncore.Engine) *http.ServeMux {
return newServeMuxWithCallerAuth(engine, callerauth.Disabled())
}
func newServeMuxWithCallerAuth(engine *decisioncore.Engine, authenticator *callerauth.Authenticator) *http.ServeMux {
return newServeMuxWithServicePilotInputs(engine, authenticator, nil)
}
func newServeMuxWithServicePilotInputs(engine *decisioncore.Engine, authenticator *callerauth.Authenticator, serviceInputs *taskexecution.ServicePilotInputs) *http.ServeMux {
return newServeMuxWithTaskFacts(engine, authenticator, serviceInputs, nil)
}
func newServeMuxWithTaskFacts(engine *decisioncore.Engine, authenticator *callerauth.Authenticator, serviceInputs *taskexecution.ServicePilotInputs, approvalReader *taskexecution.ApprovalReader) *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("content-type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
mux.HandleFunc("/v1/check", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if engine.IsTaskPin() {
if serviceInputs == nil {
http.Error(w, "service input unavailable", http.StatusServiceUnavailable)
return
}
decode := taskexecution.DecodeServiceRequest
if engine.IsTrustedServicePilot() {
decode = taskexecution.DecodeTrustedServiceRequest
}
request, err := decode(r.Body)
if err != nil || request.Purpose != "" {
http.Error(w, "invalid service task request", http.StatusBadRequest)
return
}
input, err := serviceInputs.Prepare(r.Context(), r.Header.Get("Authorization"), request, time.Now().UTC())
if err != nil {
http.Error(w, "service task input refused", http.StatusForbidden)
return
}
var decision api.DecisionEnvelope
if engine.IsTrustedServicePilot() {
decision, err = engine.CheckTrustedService(r.Context(), input, approvalReader)
} else {
decision, err = engine.CheckServicePilot(r.Context(), input)
}
writeHTTP(w, decision, err)
return
}
var request api.CheckRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if request.Purpose != "" {
// Only the readback pin issues purpose-bearing decisions
// (FLEX-DEC-2026-019); an enforcement pin never does.
http.Error(w, "purpose is not accepted on /v1/check", http.StatusBadRequest)
return
}
identity, err := authenticator.Authorize(r.Context(), r.Header.Get("Authorization"), []string{request.Resource.System})
if err != nil {
writeCallerAuthError(w, err)
return
}
decision, err := engine.Check(callerauth.WithRecord(r.Context(), authenticator.Record(identity)), request)
writeHTTP(w, decision, err)
})
mux.HandleFunc("/v1/batch_check", func(w http.ResponseWriter, r *http.Request) {
if engine.IsTaskPin() {
http.Error(w, "service task batch unavailable", http.StatusForbidden)
return
}
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var request api.BatchCheckRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
systems := make([]string, 0, len(request.Resources))
for _, resource := range request.Resources {
systems = append(systems, resource.System)
}
identity, err := authenticator.Authorize(r.Context(), r.Header.Get("Authorization"), systems)
if err != nil {
writeCallerAuthError(w, err)
return
}
decisions, err := engine.BatchCheck(callerauth.WithRecord(r.Context(), authenticator.Record(identity)), request)
writeHTTP(w, decisions, err)
})
return mux
}
// Only the existing exact service pilot package selects this source path.
// This is not an eligibility toggle; successful preparation still only DENYs.
func buildServicePilotInputs(engine *decisioncore.Engine, mode callerauth.Mode, audience, kubernetesURL, reviewerTokenFile, caFile string, bindings map[string]string) (*taskexecution.ServicePilotInputs, error) {
if !engine.IsTaskPin() {
return nil, nil
}
if mode != callerauth.ModeEnforce || audience != "flex-auth" || len(bindings) != 1 || bindings["secrets-engine"] != taskexecution.ServicePilotPrincipal {
return nil, fmt.Errorf("service pilot requires exact enforced retained caller mapping")
}
reviewer, err := callerauth.NewKubernetesTokenReviewer(kubernetesURL, audience, reviewerTokenFile, caFile)
if err != nil {
return nil, err
}
if engine.IsTrustedServicePilot() {
return taskexecution.NewTrustedServiceInputs(reviewer)
}
return taskexecution.NewServicePilotInputs(reviewer)
}
func buildCallerAuthenticator(mode callerauth.Mode, audience, kubernetesURL, reviewerTokenFile, caFile string, bindings map[string]string, stderr io.Writer) (*callerauth.Authenticator, error) {
if mode == callerauth.ModeDisabled {
return callerauth.New(mode, nil, "", nil, nil)
}
reviewer, err := callerauth.NewKubernetesTokenReviewer(kubernetesURL, audience, reviewerTokenFile, caFile)
if err != nil {
return nil, err
}
return callerauth.New(mode, reviewer, audience, bindings, func(format string, args ...any) {
fmt.Fprintf(stderr, format+"\n", args...)
})
}
func writeCallerAuthError(w http.ResponseWriter, err error) {
switch {
case errors.Is(err, callerauth.ErrUnauthenticated):
http.Error(w, "unauthenticated", http.StatusUnauthorized)
case errors.Is(err, callerauth.ErrForbidden):
http.Error(w, "forbidden", http.StatusForbidden)
default:
http.Error(w, "caller authentication unavailable", http.StatusServiceUnavailable)
}
}
func buildEngine(ctx context.Context, registryPath, policyPath, logPath string) (*decisioncore.Engine, error) {
pkg, err := policy.LoadAndValidateFile(ctx, policyPath)
if err != nil {
return nil, err
}
load := registry.LoadFile
if pkg.Metadata.ID == taskexecution.ServicePilotPackageID && pkg.Metadata.Version == taskexecution.TrustedServicePilotPackageVersion {
load = registry.LoadFilePreservingNumbers
}
store, err := load(registryPath)
if err != nil {
return nil, err
}
engine, err := decisioncore.NewEngine(store, pkg)
if err != nil {
return nil, err
}
if logPath != "" {
engine.SetDecisionLog(audit.NewJSONLDecisionLog(logPath))
}
return engine, nil
}
func validateDescriptor(descriptor api.CaringAccessDescriptor) error {
if descriptor.Profile != api.CaringProfileCaring040RC2 {
return fmt.Errorf("unsupported CARING profile %q", descriptor.Profile)
}
if descriptor.SubjectType == "" {
return fmt.Errorf("subject_type is required")
}
if descriptor.OrganizationRelation == "" {
return fmt.Errorf("organization_relation is required")
}
if descriptor.CanonicalRole == "" {
return fmt.Errorf("canonical_role is required")
}
if descriptor.Scope.Level == "" || descriptor.Scope.ID == "" {
return fmt.Errorf("scope.level and scope.id are required")
}
if len(descriptor.Planes) == 0 {
return fmt.Errorf("at least one plane is required")
}
if len(descriptor.Capabilities) == 0 {
return fmt.Errorf("at least one capability is required")
}
return nil
}
func readYAML(path string, out any) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}
if err := yaml.Unmarshal(data, out); err != nil {
return fmt.Errorf("unmarshal %s: %w", path, err)
}
return nil
}
func writeJSON(w io.Writer, value any) error {
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
if err := encoder.Encode(value); err != nil {
return err
}
return nil
}
func writeStatus(w io.Writer, status string, extra map[string]any) int {
out := map[string]any{"status": status}
for key, value := range extra {
out[key] = value
}
if err := writeJSON(w, out); err != nil {
return fail(w, err)
}
return 0
}
// mountEmission exposes the local half of reconciliation: committed and
// released-uncommitted counts per class. Counts only, never records.
func mountEmission(mux *http.ServeMux, outbox *emission.Outbox) {
mux.HandleFunc("/v1/emission", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
writeHTTP(w, outbox.Status(), nil)
})
}
func writeHTTP(w http.ResponseWriter, value any, err error) {
w.Header().Set("content-type", "application/json")
if errors.Is(err, decisioncore.ErrRecordNotCommitted) || errors.Is(err, decisioncore.ErrServicePilotUnavailable) {
// Unavailable evaluation is not an authorization verdict (GH-DEC-2026-024).
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(value)
}
func newFlagSet(name string, stderr io.Writer) *flag.FlagSet {
fs := flag.NewFlagSet(name, flag.ContinueOnError)
fs.SetOutput(stderr)
return fs
}
func fail(stderr io.Writer, err error) int {
fmt.Fprintln(stderr, "access-engine:", err)
return 1
}
func printUsage(w io.Writer) {
fmt.Fprintln(w, "usage: access-engine <command> [options]")
fmt.Fprintln(w, "commands: version, validate, load-registry, test-policy, check, batch-check, list-allowed, explain, serve, serve-readback")
}
type keyValueFlags []string
func (f *keyValueFlags) String() string {
return strings.Join(*f, ",")
}
func (f *keyValueFlags) Set(value string) error {
if !strings.Contains(value, "=") {
return fmt.Errorf("filter must be key=value")
}
*f = append(*f, value)
return nil
}
func (f keyValueFlags) Map() map[string]any {
out := make(map[string]any, len(f))
for _, item := range f {
key, value, _ := strings.Cut(item, "=")
out[key] = value
}
return out
}
func (f keyValueFlags) StringMap() map[string]string {
out := make(map[string]string, len(f))
for _, item := range f {
key, value, _ := strings.Cut(item, "=")
out[key] = value
}
return out
}

View file

@ -0,0 +1,729 @@
package decision
import (
"bytes"
"context"
"crypto/ed25519"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"
"slices"
"sort"
"strings"
"sync"
"time"
"github.com/netkingdom/access-engine/internal/callerauth"
"github.com/netkingdom/access-engine/internal/policy"
"github.com/netkingdom/access-engine/internal/registry"
"github.com/netkingdom/access-engine/internal/sign"
"github.com/netkingdom/access-engine/internal/taskexecution"
"github.com/netkingdom/access-engine/pkg/api"
)
// Engine evaluates deterministic standalone authorization checks against a
// local registry and one validated policy package.
type Engine struct {
store *registry.Store
policy *policy.Package
mu sync.RWMutex
history map[string]api.DecisionEnvelope
log DecisionRecorder
clock func() time.Time
signingKey ed25519.PrivateKey
signingKID string
}
// DecisionRecorder persists decision envelopes.
type DecisionRecorder interface {
Append(api.DecisionEnvelope) error
}
// BatchRecorder commits several decisions in one durable write.
type BatchRecorder interface {
AppendBatch([]api.DecisionEnvelope) error
}
// ErrRecordNotCommitted means a decision was withheld because its required
// record did not commit (GH-DEC-2026-024). The service answers 503; this is
// unavailability, never a substitute authorization verdict.
var ErrRecordNotCommitted = errors.New("decision withheld: record not committed")
// ListAllowedRequest describes a deterministic list_allowed call.
type ListAllowedRequest struct {
Subject api.SubjectRef `json:"subject"`
Action string `json:"action"`
System string `json:"system,omitempty"`
ResourceType string `json:"resource_type,omitempty"`
Filters map[string]any `json:"filters,omitempty"`
Context map[string]any `json:"context,omitempty"`
PolicyVersion string `json:"policy_version,omitempty"`
}
// Explanation is a compact explanation view over a recorded decision.
type Explanation struct {
DecisionID string `json:"decision_id"`
Effect api.DecisionEffect `json:"effect"`
Reason string `json:"reason,omitempty"`
Summary string `json:"summary"`
Subject api.SubjectRef `json:"subject"`
Resource api.ResourceRef `json:"resource"`
PolicyPackage string `json:"policy_package,omitempty"`
PolicyVersion string `json:"policy_version,omitempty"`
MatchedRule string `json:"matched_rule,omitempty"`
Diagnostics map[string]any `json:"diagnostics,omitempty"`
Caring *api.CaringDecisionMetadata `json:"caring,omitempty"`
}
// NewEngine creates a standalone decision engine.
func NewEngine(store *registry.Store, policyPackage *policy.Package) (*Engine, error) {
if store == nil {
return nil, fmt.Errorf("registry store is required")
}
if policyPackage == nil {
return nil, fmt.Errorf("policy package is required")
}
if !policyPackage.Valid {
return nil, fmt.Errorf("policy package %q is not valid", policyPackage.Metadata.ID)
}
return &Engine{
store: store,
policy: policyPackage,
history: make(map[string]api.DecisionEnvelope),
}, nil
}
// SetEnvelopeSigningKey installs an owner-custodied key before serving requests.
// The key is copied so caller mutation cannot change the admitted signer.
func (e *Engine) SetEnvelopeSigningKey(key ed25519.PrivateKey, kid string) error {
if len(key) != ed25519.PrivateKeySize || strings.TrimSpace(kid) == "" {
return fmt.Errorf("valid Ed25519 private key and kid are required")
}
e.mu.Lock()
defer e.mu.Unlock()
e.signingKey = append(ed25519.PrivateKey(nil), key...)
e.signingKID = kid
return nil
}
// SetDecisionLog attaches a local decision recorder to the engine.
func (e *Engine) SetDecisionLog(log DecisionRecorder) {
e.mu.Lock()
defer e.mu.Unlock()
e.log = log
}
// SetClock overrides the engine clock. Tests use this to pin allow lifetimes.
func (e *Engine) SetClock(clock func() time.Time) {
e.mu.Lock()
defer e.mu.Unlock()
e.clock = clock
}
func (e *Engine) now() time.Time {
e.mu.RLock()
clock := e.clock
e.mu.RUnlock()
if clock != nil {
return clock().UTC()
}
return time.Now().UTC()
}
// decide evaluates one request without recording it.
func (e *Engine) decide(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
normalized, facts := e.normalizeRequest(request)
expectation, err := e.policy.Evaluate(ctx, normalized)
if err != nil {
return api.DecisionEnvelope{}, err
}
envelope := e.envelope(ctx, normalized, request, expectation, facts)
e.mu.RLock()
key, kid := e.signingKey, e.signingKID
e.mu.RUnlock()
if len(key) != 0 {
if err := sign.Sign(&envelope, key, kid); err != nil {
return api.DecisionEnvelope{}, fmt.Errorf("decision signing failed: %w", err)
}
}
return envelope, nil
}
// IsServicePilot selects the existing exact source package, never a request flag.
func (e *Engine) IsServicePilot() bool {
return e.policy.Metadata.ID == taskexecution.ServicePilotPackageID && e.policy.Metadata.Version == taskexecution.ServicePilotPackageVersion
}
func (e *Engine) IsTrustedServicePilot() bool {
return e.policy.Metadata.ID == taskexecution.ServicePilotPackageID && e.policy.Metadata.Version == taskexecution.TrustedServicePilotPackageVersion
}
func (e *Engine) IsTaskPin() bool { return e.IsServicePilot() || e.IsTrustedServicePilot() }
// ErrServicePilotUnavailable withholds any result that is not this B1 slice's
// signed DENY. Provider joins and executable eligibility remain unimplemented.
var ErrServicePilotUnavailable = errors.New("service pilot source path unavailable")
// CheckServicePilot records only a signed DENY after authenticating the exact
// immutable service input. It does not validate Approval facts or grant an act.
func (e *Engine) CheckServicePilot(ctx context.Context, input taskexecution.ServiceInput) (api.DecisionEnvelope, error) {
if !e.IsServicePilot() {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
request, err := input.RequestAt(e.now())
if err != nil {
return api.DecisionEnvelope{}, err
}
envelope, err := e.decide(callerauth.WithRecord(ctx, input.Caller()), request)
if err != nil {
return api.DecisionEnvelope{}, err
}
if envelope.Effect != api.DecisionEffectDeny || envelope.Signature == nil || envelope.Signature.Mode != api.EnvelopeSignatureSigned {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
if err := e.recordDecisions([]api.DecisionEnvelope{envelope}); err != nil {
return api.DecisionEnvelope{}, err
}
return envelope, nil
}
// Check evaluates one subject/action/resource request. The decision is
// released only after its required record commits (GH-DEC-2026-024).
func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
if e.IsTaskPin() || request.Action == "execute-ccr" {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
decision, err := e.decide(ctx, request)
if err != nil {
return api.DecisionEnvelope{}, err
}
if err := e.recordDecisions([]api.DecisionEnvelope{decision}); err != nil {
return api.DecisionEnvelope{}, err
}
return decision, nil
}
// BatchCheck evaluates one subject/action/context tuple against resources in
// request order, and commits the whole batch in one durable write.
func (e *Engine) BatchCheck(ctx context.Context, request api.BatchCheckRequest) ([]api.DecisionEnvelope, error) {
if e.IsTaskPin() || request.Action == "execute-ccr" {
return nil, ErrServicePilotUnavailable
}
decisions := make([]api.DecisionEnvelope, 0, len(request.Resources))
for _, resource := range request.Resources {
decision, err := e.decide(ctx, api.CheckRequest{
ID: request.ID,
Tenant: request.Tenant,
Subject: request.Subject,
Action: request.Action,
Resource: resource,
Context: request.Context,
PolicyVersion: request.PolicyVersion,
})
if err != nil {
return nil, err
}
decisions = append(decisions, decision)
}
if err := e.recordDecisions(decisions); err != nil {
return nil, err
}
return decisions, nil
}
// ListAllowed evaluates candidate resources and returns only allow decisions.
func (e *Engine) ListAllowed(ctx context.Context, request ListAllowedRequest) ([]api.DecisionEnvelope, error) {
if e.IsTaskPin() || request.Action == "execute-ccr" {
return nil, ErrServicePilotUnavailable
}
candidates := e.store.ResourceRefs(request.System, request.ResourceType)
decisions := make([]api.DecisionEnvelope, 0, len(candidates))
for _, resource := range candidates {
if !resourceMatchesFilters(resource, request.Filters) {
continue
}
decision, err := e.decide(ctx, api.CheckRequest{
Subject: request.Subject,
Action: request.Action,
Resource: resource,
Context: request.Context,
PolicyVersion: request.PolicyVersion,
})
if err != nil {
return nil, err
}
decisions = append(decisions, decision)
}
if err := e.recordDecisions(decisions); err != nil {
return nil, err
}
allowed := make([]api.DecisionEnvelope, 0, len(decisions))
for _, decision := range decisions {
if decision.Effect == api.DecisionEffectAllow {
allowed = append(allowed, decision)
}
}
return allowed, nil
}
// Explain returns a CARING-aware explanation for a decision recorded by this
// engine instance. P2.6 replaces this in-memory history with the local log.
func (e *Engine) Explain(decisionID string) (Explanation, error) {
e.mu.RLock()
decision, ok := e.history[decisionID]
e.mu.RUnlock()
if !ok {
return Explanation{}, fmt.Errorf("decision %q not found", decisionID)
}
return ExplainEnvelope(decision), nil
}
// ExplainEnvelope returns the same explanation shape for an already-loaded
// decision envelope.
func ExplainEnvelope(decision api.DecisionEnvelope) Explanation {
return Explanation{
DecisionID: decision.ID,
Effect: decision.Effect,
Reason: decision.Reason,
Summary: explanationSummary(decision),
Subject: decision.Subject,
Resource: decision.Resource,
PolicyPackage: decision.Provenance.PolicyPackage,
PolicyVersion: decision.Provenance.PolicyVersion,
MatchedRule: decision.MatchedRule,
Diagnostics: decision.Diagnostics,
Caring: decision.Caring,
}
}
type registryFacts struct {
subjectFound bool
resourceFound bool
subject api.Subject
resource api.Resource
matchedRelationship string
descriptor *api.CaringAccessDescriptor
// overriddenByRegistry names request attribute keys whose caller-supplied
// value was displaced by a registry fact. Empty on an honest request; a
// non-empty list means the caller asserted something the registry contradicts,
// which is worth seeing even though the registry won (FLEX-DEC-2026-012).
overriddenByRegistry []string
}
func (e *Engine) normalizeRequest(request api.CheckRequest) (api.CheckRequest, registryFacts) {
normalized := request
facts := registryFacts{}
overridden := []string{}
if normalized.Tenant != "" {
if normalized.Subject.Tenant == "" {
normalized.Subject.Tenant = normalized.Tenant
}
if normalized.Resource.Tenant == "" {
normalized.Resource.Tenant = normalized.Tenant
}
}
if subject, ok := e.store.Subject(request.Subject.ID); ok {
facts.subjectFound = true
facts.subject = subject
normalized.Subject = enrichSubjectRef(request.Subject, subject, &overridden)
}
if resource, ok := e.store.Resource(request.Resource.System, request.Resource.ID); ok {
facts.resourceFound = true
facts.resource = resource
normalized.Resource = enrichResourceRef(request.Resource, resource, &overridden)
}
if normalized.CaringContext != nil {
descriptor := *normalized.CaringContext
facts.descriptor = &descriptor
facts.overriddenByRegistry = overridden
return normalized, facts
}
facts.overriddenByRegistry = overridden
if descriptor, relationshipID := e.matchCaringDescriptor(normalized, facts); descriptor != nil {
facts.descriptor = descriptor
facts.matchedRelationship = relationshipID
normalized.CaringContext = descriptor
} else if facts.resourceFound && facts.resource.Caring != nil {
descriptor := *facts.resource.Caring
facts.descriptor = &descriptor
normalized.CaringContext = &descriptor
}
return normalized, facts
}
func (e *Engine) matchCaringDescriptor(request api.CheckRequest, facts registryFacts) (*api.CaringAccessDescriptor, string) {
candidates := []string{request.Subject.ID}
if facts.subjectFound {
candidates = append(candidates, facts.subject.Groups...)
}
for _, relationship := range e.store.RelationshipsForObject(request.Resource.ID) {
if relationship.Caring == nil {
continue
}
if !slices.Contains(candidates, relationship.Subject) {
continue
}
if relationship.System != "" && relationship.System != request.Resource.System {
continue
}
if relationship.Tenant != "" && request.Resource.Tenant != "" && relationship.Tenant != request.Resource.Tenant {
continue
}
descriptor := *relationship.Caring
return &descriptor, relationship.ID
}
return nil, ""
}
// enrichSubjectRef overlays registry facts onto the subject the caller sent.
//
// Registry facts WIN. A value in the registry is an authority statement; the
// same key on the request is the caller's proposal about itself. Letting the
// proposal survive made a registry ceiling advisory — see FLEX-DEC-2026-012,
// where a caller supplying resource.attributes.allowed_subjects authorized a
// subject the registry did not know.
//
// Keys the registry does not define still pass through from the request. That
// residual is stated in docs/request-enrichment.md rather than fixed here: a
// policy reading a key its manifest omits is consuming caller input, and the
// structural fix is separating the two namespaces (FLEX-WP-0025).
func enrichSubjectRef(ref api.SubjectRef, subject api.Subject, overridden *[]string) api.SubjectRef {
out := ref
// subject.Type is deliberately NOT overwritten, unlike every other registry
// fact here. The registry's Type is CARING vocabulary (Human, Agent,
// Automation, Service); the request's is the protected system's actor
// vocabulary (service, adm, agt, atm). They are two different fields sharing
// a name, and substituting one for the other is translation rather than
// identity — the error GH-DEC-2026-008 ruled against. Making the registry win
// here denied every secrets-engine allow, because "Service" is not "service".
//
// The residual is that a caller can still assert its own subject.type and
// packages branch on it. That is not fixable by overwriting from a different
// vocabulary; it needs the two fields separated. FLEX-WP-0025.
if out.Type == "" {
out.Type = subject.Type
}
if subject.Tenant != "" {
out.Tenant = subject.Tenant
}
out.Attributes = copyMap(out.Attributes)
setAuthoritative(out.Attributes, "display_name", subject.DisplayName, "subject", overridden)
setAuthoritative(out.Attributes, "organization_relation", subject.OrganizationRelation, "subject", overridden)
setAuthoritative(out.Attributes, "roles", subject.Roles, "subject", overridden)
setAuthoritative(out.Attributes, "groups", subject.Groups, "subject", overridden)
setAuthoritatives(out.Attributes, subject.Claims, "subject", overridden)
setAuthoritatives(out.Attributes, subject.Metadata, "subject", overridden)
return out
}
// enrichResourceRef overlays registry facts onto the resource the caller sent.
// Registry facts win, for the reason given on enrichSubjectRef: ops-warden's
// max_ttl_hours, allowed_subjects, and allowed_principals are ceilings and
// allowlists, and a ceiling a caller can raise is not a ceiling.
func enrichResourceRef(ref api.ResourceRef, resource api.Resource, overridden *[]string) api.ResourceRef {
out := ref
if resource.Type != "" {
out.Type = resource.Type
}
out.Attributes = copyMap(out.Attributes)
setAuthoritative(out.Attributes, "path", resource.Path, "resource", overridden)
setAuthoritative(out.Attributes, "parent", resource.Parent, "resource", overridden)
setAuthoritative(out.Attributes, "labels", resource.Labels, "resource", overridden)
setAuthoritative(out.Attributes, "trust_zone", resource.TrustZone, "resource", overridden)
setAuthoritative(out.Attributes, "owner", resource.Owner, "resource", overridden)
setAuthoritatives(out.Attributes, resource.Attributes, "resource", overridden)
return out
}
func (e *Engine) envelope(ctx context.Context, request, submitted api.CheckRequest, expectation api.DecisionExpectation, facts registryFacts) api.DecisionEnvelope {
envelope := api.DecisionEnvelope{
RequestID: request.ID,
Effect: expectation.Effect,
Reason: expectation.Reason,
MatchedPolicyVersion: e.policy.Metadata.Version,
MatchedRule: expectation.Reason,
Resource: request.Resource,
Subject: request.Subject,
Binding: api.NewDecisionBindingFor(request, submitted),
Obligations: expectation.Obligations,
Diagnostics: map[string]any{
"action": request.Action,
"policy_package": e.policy.Metadata.ID,
"policy_status": e.policy.Metadata.Status,
"registry_subject": facts.subjectFound,
"registry_resource": facts.resourceFound,
"matched_relationship": facts.matchedRelationship,
"registry_overrode": facts.overriddenByRegistry,
},
Provenance: api.DecisionProvenance{
Evaluator: "flex-auth/local",
Mode: "standalone",
PolicyPackage: e.policy.Metadata.ID,
PolicyVersion: e.policy.Metadata.Version,
PolicyPackageDigest: e.policy.Digest(),
RegistrySnapshotDigest: e.store.Digest(),
Caller: callerProvenance(ctx),
},
Caring: e.caringDecisionMetadata(facts.descriptor, expectation.ConformanceFindings),
}
api.CompleteDecision(&envelope, request, api.DecisionCompletion{
AllowTTL: e.policy.Metadata.AllowTTL,
Now: e.now(),
})
envelope.ID = decisionID(e.policy.Metadata, request, envelope)
if envelope.Signature == nil {
envelope.Signature = api.UnsignedEnvelopeSignature()
}
return envelope
}
func callerProvenance(ctx context.Context) *api.CallerProvenance {
record, ok := callerauth.RecordFromContext(ctx)
if !ok {
record = callerauth.Record{Mode: callerauth.ModeDisabled}
}
out := &api.CallerProvenance{Mode: string(record.Mode)}
if record.Principal != "" {
out.Principal = record.Principal
out.Audience = record.Audience
if !record.NotAfter.IsZero() {
out.NotAfter = record.NotAfter.UTC().Format(time.RFC3339)
}
}
return out
}
// recordDecisions releases no effect until its required record commits.
// GH-DEC-2026-024 refuses the former restrictive-effect failure exception.
// Failed decisions must not enter Explain's history as if they were released.
func (e *Engine) recordDecisions(decisions []api.DecisionEnvelope) error {
e.mu.RLock()
log := e.log
e.mu.RUnlock()
if log != nil && len(decisions) != 0 {
if err := appendAll(log, decisions); err != nil {
return fmt.Errorf("%w: %v", ErrRecordNotCommitted, err)
}
}
e.mu.Lock()
defer e.mu.Unlock()
for _, decision := range decisions {
e.history[decision.ID] = decision
}
return nil
}
func appendAll(log DecisionRecorder, decisions []api.DecisionEnvelope) error {
if batch, ok := log.(BatchRecorder); ok {
return batch.AppendBatch(decisions)
}
for _, decision := range decisions {
if err := log.Append(decision); err != nil {
return err
}
}
return nil
}
func (e *Engine) caringDecisionMetadata(descriptor *api.CaringAccessDescriptor, findings []api.CaringConformanceFinding) *api.CaringDecisionMetadata {
profile := e.policy.Metadata.Caring.Profile
if descriptor != nil && descriptor.Profile != "" {
profile = descriptor.Profile
}
metadata := &api.CaringDecisionMetadata{
Profile: profile,
ConformanceFindings: append([]api.CaringConformanceFinding(nil), findings...),
}
if descriptor == nil {
metadata.ConformanceFindings = append(metadata.ConformanceFindings, api.CaringConformanceFinding{
Code: "CARING-DESCRIPTOR-MISSING",
Severity: "warning",
Message: "no CARING descriptor matched the request",
Fields: []string{"caring_context"},
})
return metadata
}
descriptorCopy := *descriptor
metadata.Descriptor = &descriptorCopy
metadata.RestrictionsEvaluated = append([]api.Restriction(nil), descriptor.Restrictions...)
metadata.ExposureModes = append([]api.ExposureMode(nil), descriptor.ExposureModes...)
metadata.DerivedCapabilities = append([]api.CaringDerivedCapability(nil), descriptor.DerivedCapabilities...)
return metadata
}
func decisionID(metadata api.PolicyPackageMetadata, request api.CheckRequest, envelope api.DecisionEnvelope) string {
data, _ := json.Marshal(struct {
PolicyID string `json:"policy_id"`
PolicyVersion string `json:"policy_version"`
Request api.CheckRequest `json:"request"`
Effect api.DecisionEffect `json:"effect"`
Reason string `json:"reason,omitempty"`
}{
PolicyID: metadata.ID,
PolicyVersion: metadata.Version,
Request: request,
Effect: envelope.Effect,
Reason: envelope.Reason,
})
sum := sha256.Sum256(data)
return "decision:" + hex.EncodeToString(sum[:8])
}
func copyMap(in map[string]any) map[string]any {
out := make(map[string]any, len(in))
for key, value := range in {
out[key] = value
}
return out
}
// setAuthoritative writes a registry fact over whatever the request carried for
// that key, and records the key when a caller value was displaced so the
// override is visible in diagnostics rather than silent.
func setAuthoritative(target map[string]any, key string, value any, scope string, overridden *[]string) {
if isEmptyAttribute(value) {
return
}
if existing, exists := target[key]; exists && overridden != nil && !equalAttribute(existing, value) {
*overridden = append(*overridden, scope+"."+key)
}
target[key] = value
}
func setAuthoritatives(target map[string]any, attrs map[string]any, scope string, overridden *[]string) {
for _, key := range sortedKeys(attrs) {
setAuthoritative(target, key, attrs[key], scope, overridden)
}
}
func equalAttribute(a, b any) bool {
left, errLeft := json.Marshal(a)
right, errRight := json.Marshal(b)
if errLeft != nil || errRight != nil {
return false
}
return bytes.Equal(left, right)
}
func sortedKeys(attrs map[string]any) []string {
keys := make([]string, 0, len(attrs))
for key := range attrs {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
func addAttributes(target map[string]any, attrs map[string]any) {
for key, value := range attrs {
addAttribute(target, key, value)
}
}
func addAttribute(target map[string]any, key string, value any) {
if isEmptyAttribute(value) {
return
}
if _, exists := target[key]; !exists {
target[key] = value
}
}
func resourceMatchesFilters(resource api.ResourceRef, filters map[string]any) bool {
for key, want := range filters {
var got any
switch key {
case "id":
got = resource.ID
case "type", "resource_type":
got = resource.Type
case "system":
got = resource.System
case "tenant":
got = resource.Tenant
default:
got = resource.Attributes[key]
}
if !valuesEqual(got, want) {
return false
}
}
return true
}
func valuesEqual(got, want any) bool {
if reflect.DeepEqual(got, want) {
return true
}
return fmt.Sprint(got) == fmt.Sprint(want)
}
func explanationSummary(decision api.DecisionEnvelope) string {
action, _ := decision.Diagnostics["action"].(string)
actor := decision.Subject.ID
capability := action
plane := ""
if decision.Caring != nil && decision.Caring.Descriptor != nil {
descriptor := decision.Caring.Descriptor
if descriptor.OrganizationRelation != "" || descriptor.CanonicalRole != "" {
actor = strings.TrimSpace(fmt.Sprintf("%s %s", descriptor.OrganizationRelation, descriptor.CanonicalRole))
}
if len(descriptor.Capabilities) > 0 {
capability = string(descriptor.Capabilities[0])
}
if len(descriptor.Planes) > 0 {
plane = string(descriptor.Planes[0]) + " Plane "
}
}
if capability == "" {
capability = "access"
}
verb := "may"
switch decision.Effect {
case api.DecisionEffectDeny:
verb = "may not"
case api.DecisionEffectRedact:
verb = "receives redacted"
case api.DecisionEffectAuditOnly:
verb = "is audit-only for"
case api.DecisionEffectNotApplicable:
verb = "has no applicable policy for"
}
reason := decision.Reason
if reason == "" {
reason = string(decision.Effect)
}
return fmt.Sprintf("%s %s %s %sresource %s because %s.", actor, verb, capability, plane, decision.Resource.ID, reason)
}
func isEmptyAttribute(value any) bool {
switch typed := value.(type) {
case string:
return typed == ""
case api.OrganizationRelation:
return typed == ""
case []api.CanonicalRole:
return len(typed) == 0
case []string:
return len(typed) == 0
default:
return value == nil
}
}

View file

@ -0,0 +1,372 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"errors"
"fmt"
"net/url"
auth_model "forgejo.org/models/auth"
"forgejo.org/models/db"
"forgejo.org/modules/setting"
"forgejo.org/services/auth/source/oauth2"
"github.com/urfave/cli/v2"
"xorm.io/xorm"
)
var (
oauthCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
&cli.StringFlag{
Name: "provider",
Value: "",
Usage: "OAuth2 Provider",
},
&cli.StringFlag{
Name: "key",
Value: "",
Usage: "Client ID (Key)",
},
&cli.StringFlag{
Name: "secret",
Value: "",
Usage: "Client Secret",
},
&cli.StringFlag{
Name: "secret-file",
Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file",
},
&cli.StringFlag{
Name: "auto-discover-url",
Value: "",
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
},
&cli.StringFlag{
Name: "use-custom-urls",
Value: "false",
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-tenant-id",
Value: "",
Usage: "Use custom Tenant ID for OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-auth-url",
Value: "",
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-token-url",
Value: "",
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-profile-url",
Value: "",
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-email-url",
Value: "",
Usage: "Use a custom Email URL (option for GitHub)",
},
&cli.StringFlag{
Name: "icon-url",
Value: "",
Usage: "Custom icon URL for OAuth2 login source",
},
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Set to true to skip local 2fa for users authenticated by this source",
},
&cli.StringSliceFlag{
Name: "scopes",
Value: nil,
Usage: "Scopes to request when to authenticate against this OAuth2 source",
},
&cli.StringFlag{
Name: "required-claim-name",
Value: "",
Usage: "Claim name that has to be set to allow users to login with this source",
},
&cli.StringFlag{
Name: "required-claim-value",
Value: "",
Usage: "Claim value that has to be set to allow users to login with this source",
},
&cli.StringSliceFlag{
Name: "required-claim-values",
Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)",
},
&cli.StringFlag{
Name: "group-claim-name",
Value: "",
Usage: "Claim name providing group names for this source",
},
&cli.StringFlag{
Name: "admin-group",
Value: "",
Usage: "Group Claim value for administrator users",
},
&cli.StringFlag{
Name: "restricted-group",
Value: "",
Usage: "Group Claim value for restricted users",
},
&cli.StringFlag{
Name: "group-team-map",
Value: "",
Usage: "JSON mapping between groups and org teams",
},
&cli.BoolFlag{
Name: "group-team-map-removal",
Usage: "Activate automatic team membership removal depending on groups",
},
}
microcmdAuthAddOauth = &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
}
microcmdAuthUpdateOauth = &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
}
)
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
TokenURL: c.String("custom-token-url"),
AuthURL: c.String("custom-auth-url"),
ProfileURL: c.String("custom-profile-url"),
EmailURL: c.String("custom-email-url"),
Tenant: c.String("custom-tenant-id"),
}
} else {
customURLMapping = nil
}
return &oauth2.Source{
Provider: c.String("provider"),
ClientID: c.String("key"),
ClientSecret: c.String("secret"),
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"),
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
RequiredClaimValues: c.StringSlice("required-claim-values"),
GroupClaimName: c.String("group-claim-name"),
AdminGroup: c.String("admin-group"),
RestrictedGroup: c.String("restricted-group"),
GroupTeamMap: c.String("group-team-map"),
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
}
}
func runAddOauth(c *cli.Context) error {
secret, selected, err := oauthSecretInput(c)
if err != nil {
return err
}
ctx, cancel := installSignals()
defer cancel()
ctx = db.WithSensitiveSQL(ctx)
if err := initDB(ctx); err != nil {
return err
}
engine, ok := db.GetEngine(ctx).(*xorm.Engine)
if !ok || setting.Database.LogSQL || engine.Logger().IsShowSQL() {
return errors.New("OAuth configuration requires SQL logging disabled")
}
config := parseOAuth2Config(c)
if selected {
config.ClientSecret = secret
}
if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil {
return err
}
if config.RequiredClaimValues != nil && config.Provider != "openidConnect" {
return errors.New("required-claim-values requires openidConnect")
}
if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
}
}
err = auth_model.CreateSource(ctx, &auth_model.Source{
Type: auth_model.OAuth2,
Name: c.String("name"),
IsActive: true,
Cfg: config,
})
if err != nil && c.IsSet("secret-file") {
return errors.New("OAuth source creation failed")
}
return err
}
func runUpdateOauth(c *cli.Context) error {
secret, selected, err := oauthSecretInput(c)
if err != nil {
return err
}
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
defer cancel()
ctx = db.WithSensitiveSQL(ctx)
if err := initDB(ctx); err != nil {
return err
}
engine, ok := db.GetEngine(ctx).(*xorm.Engine)
if !ok || setting.Database.LogSQL || engine.Logger().IsShowSQL() {
return errors.New("OAuth configuration requires SQL logging disabled")
}
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
oAuth2Config, ok := source.Cfg.(*oauth2.Source)
if !ok {
return errors.New("authentication source is not OAuth2")
}
if c.IsSet("name") {
source.Name = c.String("name")
}
if c.IsSet("provider") {
oAuth2Config.Provider = c.String("provider")
}
if c.IsSet("key") {
oAuth2Config.ClientID = c.String("key")
}
if selected {
oAuth2Config.ClientSecret = secret
}
if c.IsSet("auto-discover-url") {
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
}
if c.IsSet("icon-url") {
oAuth2Config.IconURL = c.String("icon-url")
}
if c.IsSet("scopes") {
oAuth2Config.Scopes = c.StringSlice("scopes")
}
if c.IsSet("required-claim-name") {
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
}
if c.IsSet("required-claim-value") {
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
}
if c.IsSet("required-claim-values") {
oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values")
}
if c.IsSet("group-claim-name") {
oAuth2Config.GroupClaimName = c.String("group-claim-name")
}
if c.IsSet("admin-group") {
oAuth2Config.AdminGroup = c.String("admin-group")
}
if c.IsSet("restricted-group") {
oAuth2Config.RestrictedGroup = c.String("restricted-group")
}
if c.IsSet("group-team-map") {
oAuth2Config.GroupTeamMap = c.String("group-team-map")
}
if c.IsSet("group-team-map-removal") {
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
}
// update custom URL mapping
customURLMapping := &oauth2.CustomURLMapping{}
if oAuth2Config.CustomURLMapping != nil {
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
customURLMapping.TokenURL = c.String("custom-token-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
customURLMapping.AuthURL = c.String("custom-auth-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
customURLMapping.ProfileURL = c.String("custom-profile-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
customURLMapping.EmailURL = c.String("custom-email-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
customURLMapping.Tenant = c.String("custom-tenant-id")
}
oAuth2Config.CustomURLMapping = customURLMapping
if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil {
return err
}
if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" {
return errors.New("required-claim-values requires openidConnect")
}
source.Cfg = oAuth2Config
err = auth_model.UpdateSource(ctx, source)
if err != nil && c.IsSet("secret-file") {
return errors.New("OAuth source update failed")
}
return err
}
// Resolve before opening the DB or mutating a loaded source. Never store the
// resolved bytes in the CLI context: they must not become flag/argv diagnostics.
func oauthSecretInput(c *cli.Context) (string, bool, error) {
if c.IsSet("secret-file") {
if c.IsSet("secret") {
return "", false, errors.New("secret and secret-file are mutually exclusive")
}
value, err := readOAuthSecretFile(c.String("secret-file"))
return value, true, err
}
return c.String("secret"), c.IsSet("secret"), nil
}

View file

@ -0,0 +1,257 @@
module forgejo.org
go 1.24
toolchain go1.24.3
require (
code.forgejo.org/f3/gof3/v3 v3.10.6
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
code.forgejo.org/forgejo/levelqueue v1.0.0
code.forgejo.org/forgejo/reply v1.0.2
code.forgejo.org/go-chi/binding v1.0.0
code.forgejo.org/go-chi/cache v1.0.0
code.forgejo.org/go-chi/captcha v1.0.1
code.forgejo.org/go-chi/session v1.0.1
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/sdk/gitea v0.20.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.17.0
github.com/42wim/httpsig v1.2.2
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.1.6
github.com/PuerkitoBio/goquery v1.10.2
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/alecthomas/chroma/v2 v2.15.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.2
github.com/buildkite/terminal-to-html/v3 v3.16.8
github.com/caddyserver/certmagic v0.22.2
github.com/chi-middleware/proxy v1.1.1
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
github.com/dustin/go-humanize v1.0.1
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/emersion/go-imap v1.2.1
github.com/felixge/fgprof v0.9.5
github.com/fsnotify/fsnotify v1.8.0
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.0
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.20.14
github.com/go-sql-driver/mysql v1.9.1
github.com/go-webauthn/webauthn v0.12.2
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-github/v64 v64.0.0
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime/v2 v2.1.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.17.11
github.com/klauspost/cpuid/v2 v2.2.10
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.80.0
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-sqlite3 v1.14.28
github.com/meilisearch/meilisearch-go v0.31.0
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27
github.com/minio/minio-go/v7 v7.0.88
github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.7.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.7.3
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.6
github.com/valyala/fastjson v1.6.4
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
gitlab.com/gitlab-org/api/client-go v0.126.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.36.0
golang.org/x/image v0.25.0
golang.org/x/net v0.38.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sync v0.12.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
google.golang.org/protobuf v1.36.4
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.5.0
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
)
require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/geo v0.2.3 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/swag v0.22.7 // indirect
github.com/go-webauthn/x v0.1.20 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/libdns/libdns v0.2.3 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v3 v3.1.1 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rhysd/actionlint v1.6.27 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.31.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.25.1
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1
replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616
replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078

View file

@ -0,0 +1,18 @@
// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package db
import "context"
type sensitiveSQLKey struct{}
// WithSensitiveSQL suppresses argument, statement and driver-error disclosure
// in the application's query hooks. The caller must separately refuse raw XORM
// SQL logging; custom driver/server auditing remains an owner custody concern.
func WithSensitiveSQL(ctx context.Context) context.Context {
return context.WithValue(ctx, sensitiveSQLKey{}, true)
}
func isSensitiveSQL(ctx context.Context) bool {
sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool)
return sensitive
}

View file

@ -0,0 +1,68 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package oauth2
import (
"net/http"
"net/url"
"github.com/markbates/goth"
"github.com/markbates/goth/gothic"
)
// Callout redirects request/response pair to authenticate against the provider
func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error {
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
var querySuffix string
if codeChallengeS256 != "" {
querySuffix = "&" + url.Values{
"code_challenge_method": []string{"S256"},
"code_challenge": []string{codeChallengeS256},
}.Encode()
}
// don't use the default gothic begin handler to prevent issues when some error occurs
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
// gothic.BeginAuthHandler(response, request)
gothRWMutex.RLock()
defer gothRWMutex.RUnlock()
url, err := gothic.GetAuthURL(response, request)
if err == nil {
// hacky way to set the code_challenge, but no better way until
// https://github.com/markbates/goth/issues/516 is resolved
http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect)
}
return err
}
// Callback handles OAuth callback, resolve to a goth user and send back to original url
// this will trigger a new authentication request, but because we save it in the session we can use that
func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) {
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
if codeVerifier != "" {
// hacky way to set the code_verifier...
// Will be picked up inside CompleteUserAuth: params := req.URL.Query()
// https://github.com/markbates/goth/pull/474/files
request = request.Clone(request.Context())
q := request.URL.Query()
q.Add("code_verifier", codeVerifier)
request.URL.RawQuery = q.Encode()
}
gothRWMutex.RLock()
defer gothRWMutex.RUnlock()
user, err := gothic.CompleteUserAuth(response, request)
if err != nil {
return user, err
}
return user, nil
}

View file

@ -0,0 +1,69 @@
"""Reconstruct the complete pinned consumer with all reviewed source candidates."""
from pathlib import Path
import hashlib
import json
import shutil
import subprocess
import tarfile
HERE = Path(__file__).resolve().parent
BASE = HERE.parent
INPUTS = Path('/tmp/custodian-forgejo-successor')
WORK = Path('/tmp/custodian-forgejo-successor-attempt2')
WORK.mkdir(exist_ok=False)
SOURCE = WORK / 'source'
assert not SOURCE.exists()
for name, pin in [('forgejo-v11.0.3.tar.gz', '1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c'),
('go1.24.3.tar.gz', '3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8')]:
assert hashlib.sha256((INPUTS / name).read_bytes()).hexdigest() == pin, name
with tarfile.open(INPUTS / name) as archive:
archive.extractall(WORK, filter='data')
(WORK / 'forgejo').rename(SOURCE)
# The interrupted packet's context snapshots are empty, but its frozen patch
# and inventory retain the exact source. Recover only if both hashes match.
operation = BASE / 'apps-operation-backend-r1'
row = next(row for row in json.loads((operation / 'source-inventory.json').read_text())['paths'] if row['path'] == 'models/db/context.go')
target = SOURCE / row['path']
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['before_sha256']
original_context = target.read_bytes()
patch = (operation / 'forgejo-operation-source.patch').read_text()
start = patch.index('--- a/models/db/context.go\n')
end = patch.index('--- /dev/null\n', start)
subprocess.run(['patch', '--fuzz=0', '-p1'], input=patch[start:end], text=True, cwd=SOURCE, check=True, capture_output=True)
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['after_sha256']
recovered = HERE / 'recovered-source/models/db/context.go'
recovered.parent.mkdir(parents=True, exist_ok=True)
recovered.write_bytes(target.read_bytes())
target.write_bytes(original_context)
applied = []
for packet, before, after in [
(BASE / 'forgejo-required-claim-values-r1', 'preimages', 'postimages'),
(BASE / 'forgejo-private-input-r1', 'preimages/forgejo', 'postimages/forgejo'),
(BASE / 'forgejo-private-input-r1', 'preimages/shared', 'postimages/shared'),
(BASE / 'apps-operation-backend-r1', 'preimages', 'postimages'),
(HERE, 'preimages/forgejo', 'forgejo-source'),
]:
for item in (packet / after).rglob('*'):
if not item.is_file():
continue
name = item.relative_to(packet / after)
target = SOURCE / name
original = packet / before / name
if packet == operation and str(name) == 'models/db/context.go':
item = recovered
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['before_sha256']
original = target
if original.exists() and target.exists() and original.read_bytes() != target.read_bytes():
# Shared SQL redaction was already composed from the same source.
identical_shared = name.as_posix().startswith('models/db/') and item.read_bytes() == target.read_bytes()
expanded_hook_test = (packet == operation and str(name) == 'models/db/sensitive_sql_test.go'
and hashlib.sha256(target.read_bytes()).hexdigest() == 'f0f74c09a9e96a6529db7b9ce459a2ea37e204de38c5ad01855b80c9a992a769'
and hashlib.sha256(item.read_bytes()).hexdigest() == 'de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b')
assert identical_shared or expanded_hook_test, str(name)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(item, target)
applied.append({'packet': packet.name, 'path': str(name), 'sha256': hashlib.sha256(item.read_bytes()).hexdigest()})
shutil.copytree(HERE / 'access-engine-source/adapters/go/decisionpep', SOURCE / 'third_party/access-engine-pep')
(WORK / 'home').mkdir()
(HERE / 'consumer-reconstruction.json').write_text(json.dumps({'workspace': str(WORK), 'inputs_verified': True, 'paths': applied}, indent=2) + '\n')
print(json.dumps({'workspace': str(WORK), 'composed_paths': len(applied)}))

View file

@ -0,0 +1,102 @@
{
"target": "producer-all",
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
"returncode": 1,
"seconds": 169.79,
"argv": [
"bwrap",
"--unshare-net",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-resume-producer-all-i56scyc5",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
"/toolchain",
"--ro-bind",
"/home/worsch/go/pkg/mod",
"/modules",
"--chdir",
"/work/source",
"--clearenv",
"--setenv",
"PATH",
"/toolchain/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"off",
"--setenv",
"GOSUMDB",
"off",
"--setenv",
"GOMODCACHE",
"/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"0",
"--",
"go",
"test",
"-p",
"4",
"-json",
"./...",
"-count=1"
],
"workspace": "/tmp/custodian-resume-producer-all-i56scyc5",
"network": "disabled",
"pid_namespace": "private",
"real_credentials": false,
"canonical_owner_modified": false,
"candidate_files": {
"adapters/go/decisionpep/LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
"adapters/go/decisionpep/go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3",
"adapters/go/decisionpep/internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
"adapters/go/decisionpep/pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
"adapters/go/decisionpep/pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
"adapters/go/decisionpep/pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
"adapters/go/decisionpep/pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba",
"adapters/go/decisionpep/pkg/decisionpep/client_test.go": "bff576b84df6aea607feea19c6372f543b29dbcc74df76ed43a852c9dc3e48ee",
"adapters/go/decisionpep/producer-source.json": "09039298bb6e2ad32a6c8c9248468ae8a0e20376b52e05be5d6e5ab9bfd5ecfa",
"adapters/go/decisionpep/testsupport/signfixture/sign.go": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29",
"cmd/access-engine/forgejo_application_profile_test.go": "bb42f6695fa353fe36ebbdb05d9d12462b2a29fec54b5fd5dc7b1e07d357d225",
"cmd/access-engine/main.go": "e4b6ad3b3dd4bb72e9a908fa7eb1296c8202ae92b81922c640c632415dbd6445",
"examples/forgejo-application-access/policy.md": "1ad11259f7ea8aae160f2542d88719cdb247037a2754c339c3a83e807953fa31",
"examples/forgejo-application-access/registry.synthetic.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
"examples/forgejo-application-access/registry_snapshot.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
"internal/decision/engine.go": "ecbb73644d979b1f15e1df6945536c20507fb3d3f9640f755a7081a344782c7d",
"internal/decision/json_profile.go": "430d2c89f47d218ec843af76a9766b42e9d938a15063fc8e979f14c56306f4bc"
}
}

View file

@ -0,0 +1,140 @@
{"Time":"2026-10-17T23:06:19.442563952Z","Action":"start","Package":"github.com/netkingdom/access-engine/pkg/decisionpep"}
{"Time":"2026-10-17T23:06:19.448497431Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision"}
{"Time":"2026-10-17T23:06:19.448664574Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Output":"=== RUN TestAuthenticDecisionProducerCanonicalPrecision\n"}
{"Time":"2026-10-17T23:06:19.453799879Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Output":"--- PASS: TestAuthenticDecisionProducerCanonicalPrecision (0.01s)\n"}
{"Time":"2026-10-17T23:06:19.453867851Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Elapsed":0.01}
{"Time":"2026-10-17T23:06:19.453955742Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings"}
{"Time":"2026-10-17T23:06:19.453966716Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Output":"=== RUN TestDecisionAdverseBindings\n"}
{"Time":"2026-10-17T23:06:19.454079315Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation"}
{"Time":"2026-10-17T23:06:19.454091921Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Output":"=== RUN TestDecisionAdverseBindings/obligation\n"}
{"Time":"2026-10-17T23:06:19.456028146Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Output":"--- PASS: TestDecisionAdverseBindings/obligation (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.456096479Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Elapsed":0}
{"Time":"2026-10-17T23:06:19.456105713Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect"}
{"Time":"2026-10-17T23:06:19.456109554Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Output":"=== RUN TestDecisionAdverseBindings/effect\n"}
{"Time":"2026-10-17T23:06:19.458184694Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Output":"--- PASS: TestDecisionAdverseBindings/effect (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.458252765Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Elapsed":0}
{"Time":"2026-10-17T23:06:19.458263874Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context"}
{"Time":"2026-10-17T23:06:19.458269384Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Output":"=== RUN TestDecisionAdverseBindings/context\n"}
{"Time":"2026-10-17T23:06:19.460167821Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Output":"--- PASS: TestDecisionAdverseBindings/context (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.460238931Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Elapsed":0}
{"Time":"2026-10-17T23:06:19.460249698Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn"}
{"Time":"2026-10-17T23:06:19.460254288Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Output":"=== RUN TestDecisionAdverseBindings/caller-warn\n"}
{"Time":"2026-10-17T23:06:19.461705185Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Output":"--- PASS: TestDecisionAdverseBindings/caller-warn (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.461765916Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Elapsed":0}
{"Time":"2026-10-17T23:06:19.461783085Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired"}
{"Time":"2026-10-17T23:06:19.461788351Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Output":"=== RUN TestDecisionAdverseBindings/caller-expired\n"}
{"Time":"2026-10-17T23:06:19.462928131Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Output":"--- PASS: TestDecisionAdverseBindings/caller-expired (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.462975309Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Elapsed":0}
{"Time":"2026-10-17T23:06:19.462983732Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL"}
{"Time":"2026-10-17T23:06:19.462988502Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Output":"=== RUN TestDecisionAdverseBindings/excess-TTL\n"}
{"Time":"2026-10-17T23:06:19.46429199Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Output":"--- PASS: TestDecisionAdverseBindings/excess-TTL (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.464345003Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Elapsed":0}
{"Time":"2026-10-17T23:06:19.464353875Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator"}
{"Time":"2026-10-17T23:06:19.4643587Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Output":"=== RUN TestDecisionAdverseBindings/evaluator\n"}
{"Time":"2026-10-17T23:06:19.465901766Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Output":"--- PASS: TestDecisionAdverseBindings/evaluator (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.465970478Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Elapsed":0}
{"Time":"2026-10-17T23:06:19.46598293Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy"}
{"Time":"2026-10-17T23:06:19.465994049Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Output":"=== RUN TestDecisionAdverseBindings/policy\n"}
{"Time":"2026-10-17T23:06:19.468933438Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Output":"--- PASS: TestDecisionAdverseBindings/policy (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.469071023Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Elapsed":0}
{"Time":"2026-10-17T23:06:19.469510802Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest"}
{"Time":"2026-10-17T23:06:19.469558296Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Output":"=== RUN TestDecisionAdverseBindings/policy-digest\n"}
{"Time":"2026-10-17T23:06:19.473045303Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Output":"--- PASS: TestDecisionAdverseBindings/policy-digest (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.473092761Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Elapsed":0}
{"Time":"2026-10-17T23:06:19.473129371Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant"}
{"Time":"2026-10-17T23:06:19.473139606Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Output":"=== RUN TestDecisionAdverseBindings/tenant\n"}
{"Time":"2026-10-17T23:06:19.474352633Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Output":"--- PASS: TestDecisionAdverseBindings/tenant (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.474412255Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Elapsed":0}
{"Time":"2026-10-17T23:06:19.474419595Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action"}
{"Time":"2026-10-17T23:06:19.474424537Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Output":"=== RUN TestDecisionAdverseBindings/action\n"}
{"Time":"2026-10-17T23:06:19.475560197Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Output":"--- PASS: TestDecisionAdverseBindings/action (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.475602199Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Elapsed":0}
{"Time":"2026-10-17T23:06:19.475608276Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal"}
{"Time":"2026-10-17T23:06:19.47561228Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Output":"=== RUN TestDecisionAdverseBindings/caller-principal\n"}
{"Time":"2026-10-17T23:06:19.476730761Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Output":"--- PASS: TestDecisionAdverseBindings/caller-principal (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.47677959Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Elapsed":0}
{"Time":"2026-10-17T23:06:19.476786876Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience"}
{"Time":"2026-10-17T23:06:19.476791538Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Output":"=== RUN TestDecisionAdverseBindings/caller-audience\n"}
{"Time":"2026-10-17T23:06:19.478109229Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Output":"--- PASS: TestDecisionAdverseBindings/caller-audience (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.47815262Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Elapsed":0}
{"Time":"2026-10-17T23:06:19.478159401Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin"}
{"Time":"2026-10-17T23:06:19.478163693Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Output":"=== RUN TestDecisionAdverseBindings/future-begin\n"}
{"Time":"2026-10-17T23:06:19.479169558Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Output":"--- PASS: TestDecisionAdverseBindings/future-begin (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.479198819Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Elapsed":0}
{"Time":"2026-10-17T23:06:19.479205014Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned"}
{"Time":"2026-10-17T23:06:19.47920918Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Output":"=== RUN TestDecisionAdverseBindings/unsigned\n"}
{"Time":"2026-10-17T23:06:19.480044655Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Output":"--- PASS: TestDecisionAdverseBindings/unsigned (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.480074547Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Elapsed":0}
{"Time":"2026-10-17T23:06:19.480081553Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version"}
{"Time":"2026-10-17T23:06:19.480085963Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Output":"=== RUN TestDecisionAdverseBindings/version\n"}
{"Time":"2026-10-17T23:06:19.480985154Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Output":"--- PASS: TestDecisionAdverseBindings/version (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.48103685Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Elapsed":0}
{"Time":"2026-10-17T23:06:19.481055146Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose"}
{"Time":"2026-10-17T23:06:19.481073135Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Output":"=== RUN TestDecisionAdverseBindings/purpose\n"}
{"Time":"2026-10-17T23:06:19.482676732Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Output":"--- PASS: TestDecisionAdverseBindings/purpose (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.482733595Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Elapsed":0}
{"Time":"2026-10-17T23:06:19.482741611Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted"}
{"Time":"2026-10-17T23:06:19.482746426Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Output":"=== RUN TestDecisionAdverseBindings/submitted\n"}
{"Time":"2026-10-17T23:06:19.483891878Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Output":"--- PASS: TestDecisionAdverseBindings/submitted (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.483942474Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Elapsed":0}
{"Time":"2026-10-17T23:06:19.483949787Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision"}
{"Time":"2026-10-17T23:06:19.483961419Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Output":"=== RUN TestDecisionAdverseBindings/stale-decision\n"}
{"Time":"2026-10-17T23:06:19.485034972Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Output":"--- PASS: TestDecisionAdverseBindings/stale-decision (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.485071672Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Elapsed":0}
{"Time":"2026-10-17T23:06:19.485077461Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry"}
{"Time":"2026-10-17T23:06:19.485080626Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Output":"=== RUN TestDecisionAdverseBindings/subnanosecond-expiry\n"}
{"Time":"2026-10-17T23:06:19.486299262Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Output":"--- PASS: TestDecisionAdverseBindings/subnanosecond-expiry (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.486346044Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Elapsed":0}
{"Time":"2026-10-17T23:06:19.486352906Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL"}
{"Time":"2026-10-17T23:06:19.486357171Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Output":"=== RUN TestDecisionAdverseBindings/missing-TTL\n"}
{"Time":"2026-10-17T23:06:19.487498926Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Output":"--- PASS: TestDecisionAdverseBindings/missing-TTL (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.487526329Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Elapsed":0}
{"Time":"2026-10-17T23:06:19.487532786Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry"}
{"Time":"2026-10-17T23:06:19.487536916Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Output":"=== RUN TestDecisionAdverseBindings/registry\n"}
{"Time":"2026-10-17T23:06:19.4885092Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Output":"--- PASS: TestDecisionAdverseBindings/registry (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.488538515Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Elapsed":0}
{"Time":"2026-10-17T23:06:19.488546766Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor"}
{"Time":"2026-10-17T23:06:19.488552627Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Output":"=== RUN TestDecisionAdverseBindings/actor\n"}
{"Time":"2026-10-17T23:06:19.489720478Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Output":"--- PASS: TestDecisionAdverseBindings/actor (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.489760731Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Elapsed":0}
{"Time":"2026-10-17T23:06:19.489776944Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource"}
{"Time":"2026-10-17T23:06:19.489800533Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Output":"=== RUN TestDecisionAdverseBindings/resource\n"}
{"Time":"2026-10-17T23:06:19.490681293Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Output":"--- PASS: TestDecisionAdverseBindings/resource (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.490718823Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Elapsed":0}
{"Time":"2026-10-17T23:06:19.490727119Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile"}
{"Time":"2026-10-17T23:06:19.490732755Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Output":"=== RUN TestDecisionAdverseBindings/legacy-numeric-profile\n"}
{"Time":"2026-10-17T23:06:19.491622432Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Output":"--- PASS: TestDecisionAdverseBindings/legacy-numeric-profile (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.491666662Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Elapsed":0}
{"Time":"2026-10-17T23:06:19.491689277Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired"}
{"Time":"2026-10-17T23:06:19.491694345Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Output":"=== RUN TestDecisionAdverseBindings/expired\n"}
{"Time":"2026-10-17T23:06:19.492690363Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Output":"--- PASS: TestDecisionAdverseBindings/expired (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.492722266Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Elapsed":0}
{"Time":"2026-10-17T23:06:19.492729633Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Output":"--- PASS: TestDecisionAdverseBindings (0.04s)\n"}
{"Time":"2026-10-17T23:06:19.49273526Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Elapsed":0.04}
{"Time":"2026-10-17T23:06:19.492764701Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay"}
{"Time":"2026-10-17T23:06:19.492776244Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Output":"=== RUN TestSignatureForgeryAndReplay\n"}
{"Time":"2026-10-17T23:06:19.492788092Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload"}
{"Time":"2026-10-17T23:06:19.492792862Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Output":"=== RUN TestSignatureForgeryAndReplay/forged-payload\n"}
{"Time":"2026-10-17T23:06:19.493799896Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Output":"--- PASS: TestSignatureForgeryAndReplay/forged-payload (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.493847209Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Elapsed":0}
{"Time":"2026-10-17T23:06:19.493856587Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key"}
{"Time":"2026-10-17T23:06:19.493878878Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Output":"=== RUN TestSignatureForgeryAndReplay/wrong-key\n"}
{"Time":"2026-10-17T23:06:19.494746653Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Output":"--- PASS: TestSignatureForgeryAndReplay/wrong-key (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.494777546Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Elapsed":0}
{"Time":"2026-10-17T23:06:19.494785048Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay"}
{"Time":"2026-10-17T23:06:19.494796834Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Output":"=== RUN TestSignatureForgeryAndReplay/replay\n"}
{"Time":"2026-10-17T23:06:19.496075426Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Output":"--- PASS: TestSignatureForgeryAndReplay/replay (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.4961153Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Elapsed":0}
{"Time":"2026-10-17T23:06:19.496123596Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Output":"--- PASS: TestSignatureForgeryAndReplay (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.496129169Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Elapsed":0}
{"Time":"2026-10-17T23:06:19.496134678Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse"}
{"Time":"2026-10-17T23:06:19.496139385Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Output":"=== RUN TestUnavailableAndPublicTokenFileRefuse\n"}
{"Time":"2026-10-17T23:06:19.496836346Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Output":"--- PASS: TestUnavailableAndPublicTokenFileRefuse (0.00s)\n"}
{"Time":"2026-10-17T23:06:19.496868132Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Elapsed":0}
{"Time":"2026-10-17T23:06:19.496875598Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource"}
{"Time":"2026-10-17T23:06:19.496881252Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Output":"=== RUN TestConsumerExportMatchesProducerSource\n"}
{"Time":"2026-10-17T23:06:19.556997898Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Output":"--- PASS: TestConsumerExportMatchesProducerSource (0.06s)\n"}
{"Time":"2026-10-17T23:06:19.557063985Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Elapsed":0.06}
{"Time":"2026-10-17T23:06:19.557079477Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Output":"PASS\n"}
{"Time":"2026-10-17T23:06:19.559095012Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Output":"ok \tgithub.com/netkingdom/access-engine/pkg/decisionpep\t0.116s\n"}
{"Time":"2026-10-17T23:06:19.559188025Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Elapsed":0.117}

View file

@ -0,0 +1,104 @@
{
"target": "producer",
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
"returncode": 0,
"seconds": 65.82,
"argv": [
"bwrap",
"--unshare-net",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-resume-producer-xtoe0khs",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
"/toolchain",
"--ro-bind",
"/home/worsch/go/pkg/mod",
"/modules",
"--chdir",
"/work/source",
"--clearenv",
"--setenv",
"PATH",
"/toolchain/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"off",
"--setenv",
"GOSUMDB",
"off",
"--setenv",
"GOMODCACHE",
"/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"0",
"--",
"go",
"test",
"-p",
"4",
"-json",
"./cmd/access-engine",
"-run",
"TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals",
"-count=1"
],
"workspace": "/tmp/custodian-resume-producer-xtoe0khs",
"network": "disabled",
"pid_namespace": "private",
"real_credentials": false,
"canonical_owner_modified": false,
"candidate_files": {
"adapters/go/decisionpep/LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
"adapters/go/decisionpep/go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3",
"adapters/go/decisionpep/internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
"adapters/go/decisionpep/pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
"adapters/go/decisionpep/pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
"adapters/go/decisionpep/pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
"adapters/go/decisionpep/pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba",
"adapters/go/decisionpep/pkg/decisionpep/client_test.go": "bff576b84df6aea607feea19c6372f543b29dbcc74df76ed43a852c9dc3e48ee",
"adapters/go/decisionpep/producer-source.json": "09039298bb6e2ad32a6c8c9248468ae8a0e20376b52e05be5d6e5ab9bfd5ecfa",
"adapters/go/decisionpep/testsupport/signfixture/sign.go": "24574585bd63b603ecf256eb945b2d4a894e4432b56d885d07234b80d39fddb5",
"cmd/access-engine/forgejo_application_profile_test.go": "931464acd69c34110c3cce3ddbdf732608f1b7249f5f111209a27df205b245e1",
"cmd/access-engine/main.go": "e4b6ad3b3dd4bb72e9a908fa7eb1296c8202ae92b81922c640c632415dbd6445",
"examples/forgejo-application-access/policy.md": "1ad11259f7ea8aae160f2542d88719cdb247037a2754c339c3a83e807953fa31",
"examples/forgejo-application-access/registry.synthetic.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
"examples/forgejo-application-access/registry_snapshot.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
"internal/decision/engine.go": "ecbb73644d979b1f15e1df6945536c20507fb3d3f9640f755a7081a344782c7d",
"internal/decision/json_profile.go": "430d2c89f47d218ec843af76a9766b42e9d938a15063fc8e979f14c56306f4bc"
}
}

View file

@ -0,0 +1,85 @@
{
"target": "producer",
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
"returncode": 1,
"seconds": 66.81,
"argv": [
"bwrap",
"--unshare-net",
"--unshare-pid",
"--die-with-parent",
"--new-session",
"--ro-bind",
"/usr",
"/usr",
"--ro-bind",
"/lib",
"/lib",
"--ro-bind",
"/lib64",
"/lib64",
"--symlink",
"usr/bin",
"/bin",
"--proc",
"/proc",
"--dev",
"/dev",
"--tmpfs",
"/tmp",
"--bind",
"/tmp/custodian-resume-producer-1ir3az_t",
"/work",
"--ro-bind",
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
"/toolchain",
"--ro-bind",
"/home/worsch/go/pkg/mod",
"/modules",
"--chdir",
"/work/source",
"--clearenv",
"--setenv",
"PATH",
"/toolchain/bin:/usr/bin:/bin",
"--setenv",
"HOME",
"/work/home",
"--setenv",
"GOTOOLCHAIN",
"local",
"--setenv",
"GOPROXY",
"off",
"--setenv",
"GOSUMDB",
"off",
"--setenv",
"GOMODCACHE",
"/modules",
"--setenv",
"GOCACHE",
"/work/gocache",
"--setenv",
"GOMAXPROCS",
"4",
"--setenv",
"CGO_ENABLED",
"0",
"--",
"go",
"test",
"-p",
"4",
"-json",
"./cmd/access-engine",
"-run",
"TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals",
"-count=1"
],
"workspace": "/tmp/custodian-resume-producer-1ir3az_t",
"network": "disabled",
"pid_namespace": "private",
"real_credentials": false,
"canonical_owner_modified": false
}

View file

@ -0,0 +1,126 @@
{"Time":"2026-10-17T22:50:49.514394056Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
{"Time":"2026-10-17T22:50:49.533137271Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
{"Time":"2026-10-17T22:50:49.533213845Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
{"Time":"2026-10-17T22:50:49.595710681Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
{"Time":"2026-10-17T22:50:49.596022136Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
{"Time":"2026-10-17T22:50:49.612730726Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.02s)\n"}
{"Time":"2026-10-17T22:50:49.61278262Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.02}
{"Time":"2026-10-17T22:50:49.613566436Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
{"Time":"2026-10-17T22:50:49.613613217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
{"Time":"2026-10-17T22:50:49.630240237Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.02s)\n"}
{"Time":"2026-10-17T22:50:49.630295386Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.02}
{"Time":"2026-10-17T22:50:49.630308777Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
{"Time":"2026-10-17T22:50:49.630315558Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
{"Time":"2026-10-17T22:50:49.649236847Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.02s)\n"}
{"Time":"2026-10-17T22:50:49.64929958Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.02}
{"Time":"2026-10-17T22:50:49.649318183Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
{"Time":"2026-10-17T22:50:49.649328111Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
{"Time":"2026-10-17T22:50:49.663413081Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.663487049Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.663504985Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
{"Time":"2026-10-17T22:50:49.66351356Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
{"Time":"2026-10-17T22:50:49.677835919Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.677896956Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.677916939Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
{"Time":"2026-10-17T22:50:49.677924982Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
{"Time":"2026-10-17T22:50:49.693350107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.02s)\n"}
{"Time":"2026-10-17T22:50:49.693413931Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.02}
{"Time":"2026-10-17T22:50:49.693435563Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
{"Time":"2026-10-17T22:50:49.69345661Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
{"Time":"2026-10-17T22:50:49.706861893Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.706927268Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.706957774Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
{"Time":"2026-10-17T22:50:49.706967107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
{"Time":"2026-10-17T22:50:49.721171004Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.721239517Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.721252565Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
{"Time":"2026-10-17T22:50:49.721258985Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
{"Time":"2026-10-17T22:50:49.73814155Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.02s)\n"}
{"Time":"2026-10-17T22:50:49.738203643Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.02}
{"Time":"2026-10-17T22:50:49.738226051Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
{"Time":"2026-10-17T22:50:49.738234284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
{"Time":"2026-10-17T22:50:49.751882211Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.752014882Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.752033728Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
{"Time":"2026-10-17T22:50:49.752040897Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
{"Time":"2026-10-17T22:50:49.76587286Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.765931914Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.765961888Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
{"Time":"2026-10-17T22:50:49.765971194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
{"Time":"2026-10-17T22:50:49.779735065Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.779787906Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.801093703Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy (0.27s)\n"}
{"Time":"2026-10-17T22:50:49.801141071Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.27}
{"Time":"2026-10-17T22:50:49.803536211Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals"}
{"Time":"2026-10-17T22:50:49.803569097Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals\n"}
{"Time":"2026-10-17T22:50:49.893766116Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
{"Time":"2026-10-17T22:50:49.895266983Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect"}
{"Time":"2026-10-17T22:50:49.895314585Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/redirect\n"}
{"Time":"2026-10-17T22:50:49.903250446Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/redirect (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.903329068Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.903352819Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate"}
{"Time":"2026-10-17T22:50:49.903370863Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate\n"}
{"Time":"2026-10-17T22:50:49.913845124Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.913902419Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.91393517Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed"}
{"Time":"2026-10-17T22:50:49.913946811Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/changed\n"}
{"Time":"2026-10-17T22:50:49.920414259Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/changed (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.920481501Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.920521339Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired"}
{"Time":"2026-10-17T22:50:49.920534261Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/expired\n"}
{"Time":"2026-10-17T22:50:49.929199396Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/expired (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.929263328Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.92930102Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh"}
{"Time":"2026-10-17T22:50:49.929324771Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh\n"}
{"Time":"2026-10-17T22:50:49.957675043Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh (0.03s)\n"}
{"Time":"2026-10-17T22:50:49.957739111Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Elapsed":0.03}
{"Time":"2026-10-17T22:50:49.957763097Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale"}
{"Time":"2026-10-17T22:50:49.957771961Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale\n"}
{"Time":"2026-10-17T22:50:49.963378736Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.963429648Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Elapsed":0.01}
{"Time":"2026-10-17T22:50:49.964168162Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind"}
{"Time":"2026-10-17T22:50:49.96420993Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind\n"}
{"Time":"2026-10-17T22:50:49.97145752Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind (0.01s)\n"}
{"Time":"2026-10-17T22:50:49.971524068Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Elapsed":0.01}
{"Time":"2026-10-17T22:50:50.106157584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals (0.30s)\n"}
{"Time":"2026-10-17T22:50:50.106252464Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Elapsed":0.3}
{"Time":"2026-10-17T22:50:50.106287162Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals"}
{"Time":"2026-10-17T22:50:50.106400202Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals\n"}
{"Time":"2026-10-17T22:50:50.209683094Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
{"Time":"2026-10-17T22:50:50.21441439Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect"}
{"Time":"2026-10-17T22:50:50.214469269Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect\n"}
{"Time":"2026-10-17T22:50:50.218687389Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect (0.00s)\n"}
{"Time":"2026-10-17T22:50:50.218731366Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Elapsed":0}
{"Time":"2026-10-17T22:50:50.218874326Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate"}
{"Time":"2026-10-17T22:50:50.218895047Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate\n"}
{"Time":"2026-10-17T22:50:50.229670287Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate (0.01s)\n"}
{"Time":"2026-10-17T22:50:50.22973522Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Elapsed":0.01}
{"Time":"2026-10-17T22:50:50.229754544Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed"}
{"Time":"2026-10-17T22:50:50.229763426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed\n"}
{"Time":"2026-10-17T22:50:50.234511774Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed (0.00s)\n"}
{"Time":"2026-10-17T22:50:50.234559611Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Elapsed":0}
{"Time":"2026-10-17T22:50:50.234580594Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired"}
{"Time":"2026-10-17T22:50:50.234586626Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired\n"}
{"Time":"2026-10-17T22:50:50.241561501Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired (0.01s)\n"}
{"Time":"2026-10-17T22:50:50.241626894Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Elapsed":0.01}
{"Time":"2026-10-17T22:50:50.241646632Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh"}
{"Time":"2026-10-17T22:50:50.241655587Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh\n"}
{"Time":"2026-10-17T22:50:50.265060146Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh (0.02s)\n"}
{"Time":"2026-10-17T22:50:50.265120128Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Elapsed":0.02}
{"Time":"2026-10-17T22:50:50.265144998Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act"}
{"Time":"2026-10-17T22:50:50.265301637Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act\n"}
{"Time":"2026-10-17T22:50:50.270375383Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act (0.00s)\n"}
{"Time":"2026-10-17T22:50:50.270440064Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Elapsed":0}
{"Time":"2026-10-17T22:50:50.270458793Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale"}
{"Time":"2026-10-17T22:50:50.27047478Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale\n"}
{"Time":"2026-10-17T22:50:50.277250946Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale (0.01s)\n"}
{"Time":"2026-10-17T22:50:50.277301379Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Elapsed":0.01}
{"Time":"2026-10-17T22:50:50.277329964Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind"}
{"Time":"2026-10-17T22:50:50.277337664Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind\n"}
{"Time":"2026-10-17T22:50:50.282770016Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind (0.01s)\n"}
{"Time":"2026-10-17T22:50:50.28292834Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Elapsed":0.01}
{"Time":"2026-10-17T22:50:50.420567507Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals (0.31s)\n"}
{"Time":"2026-10-17T22:50:50.420620754Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Elapsed":0.31}
{"Time":"2026-10-17T22:50:50.420926195Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"PASS\n"}
{"Time":"2026-10-17T22:50:50.425558858Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"ok \tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.910s\n"}
{"Time":"2026-10-17T22:50:50.425685686Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.911}

View file

@ -0,0 +1,339 @@
{"Time":"2026-10-17T22:49:08.070924685Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
{"Time":"2026-10-17T22:49:08.081350257Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
{"Time":"2026-10-17T22:49:08.081401358Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
{"Time":"2026-10-17T22:49:08.124026957Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" forgejo_application_profile_test.go:39: policy: \u003cnil\u003e \u0026{Source:../../examples/forgejo-application-access/policy.md Metadata:{ID:forgejo.application-access Name:Forgejo authenticated application access Namespace:forgejo:application Version:v1 Status:draft Package:flexauth.forgejo.application Actions:[application.access] Owner:railiance-apps Fixtures:[] Caring:{Profile:caring-0.4.0-rc2 Enforce:false CanonicalRoles:[] OrganizationRelations:[] Scopes:[] Planes:[] Capabilities:[] ExposureModes:[] Conditions:[] Restrictions:[] Metadata:map[]} Activation:map[] Metadata:map[] AllowTTL:10s} Prose:This source policy enforces the original three-group any-of in the sole PDP.\n"}
{"Time":"2026-10-17T22:49:08.124075109Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The registry identifies the application tenant and admitted issuer. Fresh identity\n"}
{"Time":"2026-10-17T22:49:08.124081908Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" and UserInfo facts arrive only in request context from the admitted Forgejo caller,\n"}
{"Time":"2026-10-17T22:49:08.124087445Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" which verifies issuer JOSE and current UserInfo before this request. They are not\n"}
{"Time":"2026-10-17T22:49:08.124093162Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" static subject registry attributes. This policy requires aal2 assurance.\n"}
{"Time":"2026-10-17T22:49:08.124098356Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The PEP independently verifies the ID token and supplies fresh UserInfo groups;\n"}
{"Time":"2026-10-17T22:49:08.124103541Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" the authenticated workload caller may represent only the Forgejo system. A\n"}
{"Time":"2026-10-17T22:49:08.124108816Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.\n"}
{"Time":"2026-10-17T22:49:08.124114118Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The accompanying registry is synthetic test data, not an estate admission. RuleBlocks:[{Language:rego Tags:[] Info:rego Body:import rego.v1\n"}
{"Time":"2026-10-17T22:49:08.124123279Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124129907Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" default decision := {\"effect\": \"deny\", \"reason\": \"application_access_refused\"}\n"}
{"Time":"2026-10-17T22:49:08.124153667Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124162144Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" original_groups := {\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"}\n"}
{"Time":"2026-10-17T22:49:08.124179322Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124188429Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision := {\"effect\": \"allow\", \"reason\": \"current_authenticated_application_access\"} if {\n"}
{"Time":"2026-10-17T22:49:08.12419604Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.action == \"application.access\"\n"}
{"Time":"2026-10-17T22:49:08.124218312Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" object.get(input, \"purpose\", \"\") == \"\"\n"}
{"Time":"2026-10-17T22:49:08.124235752Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.id == \"application:forgejo\"\n"}
{"Time":"2026-10-17T22:49:08.124249584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.type == \"application\"\n"}
{"Time":"2026-10-17T22:49:08.124254814Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.system == \"forgejo\"\n"}
{"Time":"2026-10-17T22:49:08.124259828Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.tenant\n"}
{"Time":"2026-10-17T22:49:08.124264787Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.attributes.tenant\n"}
{"Time":"2026-10-17T22:49:08.124269954Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.tenant == input.tenant\n"}
{"Time":"2026-10-17T22:49:08.124274904Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_tenant == input.tenant\n"}
{"Time":"2026-10-17T22:49:08.124279837Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.id == input.context.identity_subject\n"}
{"Time":"2026-10-17T22:49:08.124284905Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == input.context.identity_principal_type\n"}
{"Time":"2026-10-17T22:49:08.12429008Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == \"human\"\n"}
{"Time":"2026-10-17T22:49:08.124307718Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_issuer == input.resource.attributes.identity_issuer\n"}
{"Time":"2026-10-17T22:49:08.124313318Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_assurance == \"aal2\"\n"}
{"Time":"2026-10-17T22:49:08.124318656Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.required_groups_any_of) == 3\n"}
{"Time":"2026-10-17T22:49:08.124323643Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {g | g := input.context.required_groups_any_of[_]} == original_groups\n"}
{"Time":"2026-10-17T22:49:08.124330144Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" some group in input.context.current_userinfo_groups\n"}
{"Time":"2026-10-17T22:49:08.124338079Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" group in original_groups\n"}
{"Time":"2026-10-17T22:49:08.124357403Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" is_string(input.context.decisionpep_request_nonce)\n"}
{"Time":"2026-10-17T22:49:08.124371759Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.decisionpep_request_nonce) == 64\n"}
{"Time":"2026-10-17T22:49:08.124379099Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" } StartLine:12}] TestBlocks:[{Language:rego Tags:[test] Info:rego test Body:package flexauth.forgejo.application_test\n"}
{"Time":"2026-10-17T22:49:08.124390614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
{"Time":"2026-10-17T22:49:08.124396682Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import data.flexauth.forgejo.application\n"}
{"Time":"2026-10-17T22:49:08.124401561Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124406385Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" valid_request := {\n"}
{"Time":"2026-10-17T22:49:08.124411254Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124416277Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
{"Time":"2026-10-17T22:49:08.124421254Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124426223Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.124440569Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
{"Time":"2026-10-17T22:49:08.124445962Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.12445084Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
{"Time":"2026-10-17T22:49:08.124455817Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
{"Time":"2026-10-17T22:49:08.124460759Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.124465809Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
{"Time":"2026-10-17T22:49:08.124470813Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.124475863Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124482193Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
{"Time":"2026-10-17T22:49:08.124488Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124493005Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
{"Time":"2026-10-17T22:49:08.124498172Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124514213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.124519173Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
{"Time":"2026-10-17T22:49:08.124524195Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124544087Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124550832Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.124561743Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
{"Time":"2026-10-17T22:49:08.124567199Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
{"Time":"2026-10-17T22:49:08.124572447Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
{"Time":"2026-10-17T22:49:08.124577388Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
{"Time":"2026-10-17T22:49:08.124582284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.1245871Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
{"Time":"2026-10-17T22:49:08.12459233Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
{"Time":"2026-10-17T22:49:08.12459728Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
{"Time":"2026-10-17T22:49:08.124602213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
{"Time":"2026-10-17T22:49:08.124609507Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.124616469Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
{"Time":"2026-10-17T22:49:08.124633475Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124641816Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124647614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124654026Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_original_groups_allow if {\n"}
{"Time":"2026-10-17T22:49:08.124660906Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" every group in [\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"] {\n"}
{"Time":"2026-10-17T22:49:08.12467812Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"allow\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [group]})})\n"}
{"Time":"2026-10-17T22:49:08.12470151Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124718706Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124727805Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124733107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_outsider_denied if {\n"}
{"Time":"2026-10-17T22:49:08.124738625Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [\"outsider\"]})})\n"}
{"Time":"2026-10-17T22:49:08.124744153Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124749031Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.12478923Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_missing_identity_denied if {\n"}
{"Time":"2026-10-17T22:49:08.124795164Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.remove(valid_request.context, [\"identity_issuer\"])})\n"}
{"Time":"2026-10-17T22:49:08.124800583Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124805389Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.124810213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_low_assurance_denied if {\n"}
{"Time":"2026-10-17T22:49:08.12481529Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"identity_assurance\": \"aal1\"})})\n"}
{"Time":"2026-10-17T22:49:08.124820655Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.124825371Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.12483217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_changed_group_contract_denied if {\n"}
{"Time":"2026-10-17T22:49:08.124839907Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"required_groups_any_of\": [\"outsider\"]})})\n"}
{"Time":"2026-10-17T22:49:08.124862252Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" } StartLine:43}] FixtureBlocks:[{Language:yaml Tags:[fixture] Info:yaml fixture Body:[\n"}
{"Time":"2026-10-17T22:49:08.124870169Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {\n"}
{"Time":"2026-10-17T22:49:08.124876671Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"allowed-original-group\",\n"}
{"Time":"2026-10-17T22:49:08.124887942Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"request\": {\n"}
{"Time":"2026-10-17T22:49:08.124899601Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124913154Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
{"Time":"2026-10-17T22:49:08.124919845Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124926879Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.124934128Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
{"Time":"2026-10-17T22:49:08.124941171Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.124947627Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
{"Time":"2026-10-17T22:49:08.12495513Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
{"Time":"2026-10-17T22:49:08.124962731Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.124969683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
{"Time":"2026-10-17T22:49:08.124975869Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.12498274Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.124988836Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
{"Time":"2026-10-17T22:49:08.125009359Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125026258Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
{"Time":"2026-10-17T22:49:08.125035194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125042426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125047953Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
{"Time":"2026-10-17T22:49:08.125055762Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125062769Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.12506808Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.125073147Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
{"Time":"2026-10-17T22:49:08.125078179Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
{"Time":"2026-10-17T22:49:08.125083265Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
{"Time":"2026-10-17T22:49:08.125088332Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
{"Time":"2026-10-17T22:49:08.125093211Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.125098035Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
{"Time":"2026-10-17T22:49:08.12510359Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
{"Time":"2026-10-17T22:49:08.125108486Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
{"Time":"2026-10-17T22:49:08.125113382Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
{"Time":"2026-10-17T22:49:08.125125691Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.125131182Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
{"Time":"2026-10-17T22:49:08.125136638Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125141489Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125146277Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"expect\": {\n"}
{"Time":"2026-10-17T22:49:08.125151264Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"effect\": \"allow\",\n"}
{"Time":"2026-10-17T22:49:08.125158802Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"reason\": \"current_authenticated_application_access\"\n"}
{"Time":"2026-10-17T22:49:08.125163825Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125169163Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125173897Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {\n"}
{"Time":"2026-10-17T22:49:08.125178694Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"missing-identity-context\",\n"}
{"Time":"2026-10-17T22:49:08.125183681Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"request\": {\n"}
{"Time":"2026-10-17T22:49:08.125188568Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125193546Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
{"Time":"2026-10-17T22:49:08.125198424Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125203393Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.125208334Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
{"Time":"2026-10-17T22:49:08.125213248Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125236143Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
{"Time":"2026-10-17T22:49:08.125241391Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
{"Time":"2026-10-17T22:49:08.125246666Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.125251734Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
{"Time":"2026-10-17T22:49:08.125256856Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.12526177Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.12526838Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
{"Time":"2026-10-17T22:49:08.125274602Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125281644Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
{"Time":"2026-10-17T22:49:08.125286712Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.12529159Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125296396Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {}\n"}
{"Time":"2026-10-17T22:49:08.125301771Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125306568Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"expect\": {\n"}
{"Time":"2026-10-17T22:49:08.125311554Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"effect\": \"deny\",\n"}
{"Time":"2026-10-17T22:49:08.125316577Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"reason\": \"application_access_refused\"\n"}
{"Time":"2026-10-17T22:49:08.1253216Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125331609Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125336676Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ] StartLine:107}] RegoModule:package flexauth.forgejo.application\n"}
{"Time":"2026-10-17T22:49:08.125341618Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125346496Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
{"Time":"2026-10-17T22:49:08.125351248Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125356217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" default decision := {\"effect\": \"deny\", \"reason\": \"application_access_refused\"}\n"}
{"Time":"2026-10-17T22:49:08.125361474Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125366668Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" original_groups := {\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"}\n"}
{"Time":"2026-10-17T22:49:08.12537188Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125376948Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision := {\"effect\": \"allow\", \"reason\": \"current_authenticated_application_access\"} if {\n"}
{"Time":"2026-10-17T22:49:08.125382241Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.action == \"application.access\"\n"}
{"Time":"2026-10-17T22:49:08.125387245Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" object.get(input, \"purpose\", \"\") == \"\"\n"}
{"Time":"2026-10-17T22:49:08.125392403Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.id == \"application:forgejo\"\n"}
{"Time":"2026-10-17T22:49:08.12539748Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.type == \"application\"\n"}
{"Time":"2026-10-17T22:49:08.125402638Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.system == \"forgejo\"\n"}
{"Time":"2026-10-17T22:49:08.125407696Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.tenant\n"}
{"Time":"2026-10-17T22:49:08.125412773Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.attributes.tenant\n"}
{"Time":"2026-10-17T22:49:08.125424306Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.tenant == input.tenant\n"}
{"Time":"2026-10-17T22:49:08.125431881Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_tenant == input.tenant\n"}
{"Time":"2026-10-17T22:49:08.125443801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.id == input.context.identity_subject\n"}
{"Time":"2026-10-17T22:49:08.125459023Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == input.context.identity_principal_type\n"}
{"Time":"2026-10-17T22:49:08.125467436Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == \"human\"\n"}
{"Time":"2026-10-17T22:49:08.125523523Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_issuer == input.resource.attributes.identity_issuer\n"}
{"Time":"2026-10-17T22:49:08.125539123Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_assurance == \"aal2\"\n"}
{"Time":"2026-10-17T22:49:08.125544308Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.required_groups_any_of) == 3\n"}
{"Time":"2026-10-17T22:49:08.125551341Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {g | g := input.context.required_groups_any_of[_]} == original_groups\n"}
{"Time":"2026-10-17T22:49:08.125559042Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" some group in input.context.current_userinfo_groups\n"}
{"Time":"2026-10-17T22:49:08.125566598Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" group in original_groups\n"}
{"Time":"2026-10-17T22:49:08.125573767Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" is_string(input.context.decisionpep_request_nonce)\n"}
{"Time":"2026-10-17T22:49:08.125581423Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.decisionpep_request_nonce) == 64\n"}
{"Time":"2026-10-17T22:49:08.125586806Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125592117Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" TestModule:package flexauth.forgejo.application_test\n"}
{"Time":"2026-10-17T22:49:08.125597095Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125601874Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
{"Time":"2026-10-17T22:49:08.125617392Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import data.flexauth.forgejo.application\n"}
{"Time":"2026-10-17T22:49:08.125629683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125634823Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" valid_request := {\n"}
{"Time":"2026-10-17T22:49:08.125639665Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125644688Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
{"Time":"2026-10-17T22:49:08.125649701Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125654607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.125659539Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
{"Time":"2026-10-17T22:49:08.125664544Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125669332Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
{"Time":"2026-10-17T22:49:08.125677628Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
{"Time":"2026-10-17T22:49:08.125684634Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.125689783Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
{"Time":"2026-10-17T22:49:08.125695581Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
{"Time":"2026-10-17T22:49:08.125700955Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125706203Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
{"Time":"2026-10-17T22:49:08.125711587Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125716699Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
{"Time":"2026-10-17T22:49:08.125730712Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125736249Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
{"Time":"2026-10-17T22:49:08.125741163Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
{"Time":"2026-10-17T22:49:08.125755861Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.12576101Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
{"Time":"2026-10-17T22:49:08.125766105Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
{"Time":"2026-10-17T22:49:08.125771173Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
{"Time":"2026-10-17T22:49:08.125776159Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
{"Time":"2026-10-17T22:49:08.125782543Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
{"Time":"2026-10-17T22:49:08.125787683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
{"Time":"2026-10-17T22:49:08.125792553Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.12579744Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
{"Time":"2026-10-17T22:49:08.125803139Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
{"Time":"2026-10-17T22:49:08.125808062Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
{"Time":"2026-10-17T22:49:08.125813139Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
{"Time":"2026-10-17T22:49:08.125817972Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
{"Time":"2026-10-17T22:49:08.125823085Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
{"Time":"2026-10-17T22:49:08.125834203Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125839073Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.12584378Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125851174Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_original_groups_allow if {\n"}
{"Time":"2026-10-17T22:49:08.125856187Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" every group in [\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"] {\n"}
{"Time":"2026-10-17T22:49:08.125861607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"allow\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [group]})})\n"}
{"Time":"2026-10-17T22:49:08.125867297Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125872004Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125876701Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125881472Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_outsider_denied if {\n"}
{"Time":"2026-10-17T22:49:08.125886675Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [\"outsider\"]})})\n"}
{"Time":"2026-10-17T22:49:08.125892094Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125896801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.12590158Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_missing_identity_denied if {\n"}
{"Time":"2026-10-17T22:49:08.125906657Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.remove(valid_request.context, [\"identity_issuer\"])})\n"}
{"Time":"2026-10-17T22:49:08.125911923Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125924024Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.1259303Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_low_assurance_denied if {\n"}
{"Time":"2026-10-17T22:49:08.125935674Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"identity_assurance\": \"aal1\"})})\n"}
{"Time":"2026-10-17T22:49:08.12594104Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.12594581Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
{"Time":"2026-10-17T22:49:08.125950706Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_changed_group_contract_denied if {\n"}
{"Time":"2026-10-17T22:49:08.125956107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"required_groups_any_of\": [\"outsider\"]})})\n"}
{"Time":"2026-10-17T22:49:08.12596214Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
{"Time":"2026-10-17T22:49:08.125978506Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" TestPackage:flexauth.forgejo.application_test Fixtures:[{ID:allowed-original-group Request:{ID: Tenant:tenant:synthetic Subject:{ID:user:synthetic Type:human Tenant:tenant:synthetic Attributes:map[]} Action:application.access Resource:{ID:application:forgejo Type:application System:forgejo Tenant:tenant:synthetic Attributes:map[identity_issuer:https://issuer.invalid tenant:tenant:synthetic]} Context:map[current_userinfo_groups:[forge-admins] decisionpep_request_nonce:0000000000000000000000000000000000000000000000000000000000000000 identity_assurance:aal2 identity_issuer:https://issuer.invalid identity_principal_type:human identity_subject:user:synthetic identity_tenant:tenant:synthetic required_groups_any_of:[forge-admins forge-coulomb-owners forge-coulomb-readers]] CaringContext:\u003cnil\u003e PolicyVersion: Purpose:} Expect:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Metadata:map[]} {ID:missing-identity-context Request:{ID: Tenant:tenant:synthetic Sub"}
{"Time":"2026-10-17T22:49:08.125988768Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"ject:{ID:user:synthetic Type:human Tenant:tenant:synthetic Attributes:map[]} Action:application.access Resource:{ID:application:forgejo Type:application System:forgejo Tenant:tenant:synthetic Attributes:map[identity_issuer:https://issuer.invalid tenant:tenant:synthetic]} Context:map[] CaringContext:\u003cnil\u003e PolicyVersion: Purpose:} Expect:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Metadata:map[]}] Validation:{Valid:false Diagnostics:[] CaringFindings:[{Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed canonical roles Fields:[caring.canonical_roles] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed organization relations Fields:[caring.organization_relations] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed scopes Fields:[caring.scopes] Descriptor: Metadata:map[]"}
{"Time":"2026-10-17T22:49:08.126002032Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed planes Fields:[caring.planes] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed capabilities Fields:[caring.capabilities] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed exposure modes Fields:[caring.exposure_modes] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed conditions Fields:[caring.conditions] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed restrictions Fields:[caring.restrictions] Descriptor: Metadata:map[]}] Tests:[{Name:test_changed_group_contract_denied Passed:true Error:} {Name:test_low_assurance_denied Passed:true Error:} {Name:test_missing_identity_denied Passed:false Error"}
{"Time":"2026-10-17T22:49:08.126009895Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":":test rule evaluated to false or undefined} {Name:test_original_groups_allow Passed:true Error:} {Name:test_outsider_denied Passed:true Error:}] Fixtures:[{ID:allowed-original-group Passed:true Expected:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Actual:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Error:} {ID:missing-identity-context Passed:true Expected:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Actual:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Error:}]} Valid:false}\n"}
{"Time":"2026-10-17T22:49:08.126023953Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy (0.04s)\n"}
{"Time":"2026-10-17T22:49:08.126031239Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.04}
{"Time":"2026-10-17T22:49:08.126051131Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals"}
{"Time":"2026-10-17T22:49:08.12605674Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals\n"}
{"Time":"2026-10-17T22:49:08.202270945Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
{"Time":"2026-10-17T22:49:08.204055053Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect"}
{"Time":"2026-10-17T22:49:08.204094017Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/redirect\n"}
{"Time":"2026-10-17T22:49:08.208897544Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/redirect (0.00s)\n"}
{"Time":"2026-10-17T22:49:08.20896887Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Elapsed":0}
{"Time":"2026-10-17T22:49:08.208984912Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate"}
{"Time":"2026-10-17T22:49:08.208992595Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate\n"}
{"Time":"2026-10-17T22:49:08.218181791Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.218242747Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.218278041Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed"}
{"Time":"2026-10-17T22:49:08.218291864Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/changed\n"}
{"Time":"2026-10-17T22:49:08.224853212Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/changed (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.224898136Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.224909984Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired"}
{"Time":"2026-10-17T22:49:08.224915584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/expired\n"}
{"Time":"2026-10-17T22:49:08.229229607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/expired (0.00s)\n"}
{"Time":"2026-10-17T22:49:08.229282024Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Elapsed":0}
{"Time":"2026-10-17T22:49:08.229308571Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh"}
{"Time":"2026-10-17T22:49:08.229317426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh\n"}
{"Time":"2026-10-17T22:49:08.248287502Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh (0.02s)\n"}
{"Time":"2026-10-17T22:49:08.248353689Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Elapsed":0.02}
{"Time":"2026-10-17T22:49:08.248370686Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale"}
{"Time":"2026-10-17T22:49:08.248376566Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale\n"}
{"Time":"2026-10-17T22:49:08.253598824Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.253636814Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.253644641Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind"}
{"Time":"2026-10-17T22:49:08.253649366Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind\n"}
{"Time":"2026-10-17T22:49:08.257235488Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind (0.00s)\n"}
{"Time":"2026-10-17T22:49:08.257274596Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Elapsed":0}
{"Time":"2026-10-17T22:49:08.364592231Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals (0.24s)\n"}
{"Time":"2026-10-17T22:49:08.364652466Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Elapsed":0.24}
{"Time":"2026-10-17T22:49:08.364672593Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals"}
{"Time":"2026-10-17T22:49:08.364681754Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals\n"}
{"Time":"2026-10-17T22:49:08.457647438Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
{"Time":"2026-10-17T22:49:08.459500203Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect"}
{"Time":"2026-10-17T22:49:08.459549032Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect\n"}
{"Time":"2026-10-17T22:49:08.464519381Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect (0.00s)\n"}
{"Time":"2026-10-17T22:49:08.464585405Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Elapsed":0}
{"Time":"2026-10-17T22:49:08.464608814Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate"}
{"Time":"2026-10-17T22:49:08.464624711Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate\n"}
{"Time":"2026-10-17T22:49:08.476276202Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.476329233Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.476353092Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed"}
{"Time":"2026-10-17T22:49:08.476360378Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed\n"}
{"Time":"2026-10-17T22:49:08.4818022Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.481870759Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.481894357Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired"}
{"Time":"2026-10-17T22:49:08.481904131Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired\n"}
{"Time":"2026-10-17T22:49:08.49030167Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.490371446Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.490396189Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh"}
{"Time":"2026-10-17T22:49:08.490405748Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh\n"}
{"Time":"2026-10-17T22:49:08.513151322Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh (0.02s)\n"}
{"Time":"2026-10-17T22:49:08.513213793Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Elapsed":0.02}
{"Time":"2026-10-17T22:49:08.513233866Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act"}
{"Time":"2026-10-17T22:49:08.51323979Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act\n"}
{"Time":"2026-10-17T22:49:08.519855417Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.519905689Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.519922019Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale"}
{"Time":"2026-10-17T22:49:08.519930197Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale\n"}
{"Time":"2026-10-17T22:49:08.525507841Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.525556011Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.52576936Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind"}
{"Time":"2026-10-17T22:49:08.525788287Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind\n"}
{"Time":"2026-10-17T22:49:08.532503419Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind (0.01s)\n"}
{"Time":"2026-10-17T22:49:08.532560778Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Elapsed":0.01}
{"Time":"2026-10-17T22:49:08.668954317Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals (0.30s)\n"}
{"Time":"2026-10-17T22:49:08.715893357Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Elapsed":0.3}
{"Time":"2026-10-17T22:49:08.715974395Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\n"}
{"Time":"2026-10-17T22:49:08.71609491Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.644s\n"}
{"Time":"2026-10-17T22:49:08.716114874Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.645}

View file

@ -0,0 +1,340 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package db
import (
"context"
"database/sql"
"xorm.io/builder"
"xorm.io/xorm"
)
// DefaultContext is the default context to run xorm queries in
// will be overwritten by Init with HammerContext
var DefaultContext context.Context
// contextKey is a value for use with context.WithValue.
type contextKey struct {
name string
}
// enginedContextKey is a context key. It is used with context.Value() to get the current Engined for the context
var (
enginedContextKey = &contextKey{"engined"}
_ Engined = &Context{}
)
// Context represents a db context
type Context struct {
context.Context
e Engine
transaction bool
}
func newContext(ctx context.Context, e Engine, transaction bool) *Context {
return &Context{
Context: ctx,
e: e,
transaction: transaction,
}
}
// InTransaction if context is in a transaction
func (ctx *Context) InTransaction() bool {
return ctx.transaction
}
// Engine returns db engine
func (ctx *Context) Engine() Engine {
return ctx.e
}
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
func (ctx *Context) Value(key any) any {
if key == enginedContextKey {
return ctx
}
return ctx.Context.Value(key)
}
// WithContext returns this engine tied to this context
func (ctx *Context) WithContext(other context.Context) *Context {
return newContext(ctx, ctx.e.Context(other), ctx.transaction)
}
// Engined structs provide an Engine
type Engined interface {
Engine() Engine
}
// GetEngine will get a db Engine from this context or return an Engine restricted to this context
func GetEngine(ctx context.Context) Engine {
if e := getEngine(ctx); e != nil {
// A private-query marker must reach XORM's real query hooks, including
// inside an existing engine/transaction context. Wrapping the Go
// context alone does not change XORM's stored session context.
if isSensitiveSQL(ctx) {
return e.Context(ctx)
}
return e
}
return x.Context(ctx)
}
// getEngine will get a db Engine from this context or return nil
func getEngine(ctx context.Context) Engine {
if engined, ok := ctx.(Engined); ok {
return engined.Engine()
}
enginedInterface := ctx.Value(enginedContextKey)
if enginedInterface != nil {
return enginedInterface.(Engined).Engine()
}
return nil
}
// Committer represents an interface to Commit or Close the Context
type Committer interface {
Commit() error
Close() error
}
// halfCommitter is a wrapper of Committer.
// It can be closed early, but can't be committed early, it is useful for reusing a transaction.
type halfCommitter struct {
committer Committer
committed bool
}
func (c *halfCommitter) Commit() error {
c.committed = true
// should do nothing, and the parent committer will commit later
return nil
}
func (c *halfCommitter) Close() error {
if c.committed {
// it's "commit and close", should do nothing, and the parent committer will commit later
return nil
}
// it's "rollback and close", let the parent committer rollback right now
return c.committer.Close()
}
// TxContext represents a transaction Context,
// it will reuse the existing transaction in the parent context or create a new one.
// Some tips to use:
//
// 1 It's always recommended to use `WithTx` in new code instead of `TxContext`, since `WithTx` will handle the transaction automatically.
// 2. To maintain the old code which uses `TxContext`:
// a. Always call `Close()` before returning regardless of whether `Commit()` has been called.
// b. Always call `Commit()` before returning if there are no errors, even if the code did not change any data.
// c. Remember the `Committer` will be a halfCommitter when a transaction is being reused.
// So calling `Commit()` will do nothing, but calling `Close()` without calling `Commit()` will rollback the transaction.
// And all operations submitted by the caller stack will be rollbacked as well, not only the operations in the current function.
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
func TxContext(parentCtx context.Context) (*Context, Committer, error) {
if sess, ok := inTransaction(parentCtx); ok {
return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil
}
sess := x.NewSession()
if err := sess.Begin(); err != nil {
sess.Close()
return nil, nil, err
}
return newContext(DefaultContext, sess, true), sess, nil
}
// WithTx represents executing database operations on a transaction, if the transaction exist,
// this function will reuse it otherwise will create a new one and close it when finished.
func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
if sess, ok := inTransaction(parentCtx); ok {
err := f(newContext(parentCtx, sess, true))
if err != nil {
// rollback immediately, in case the caller ignores returned error and tries to commit the transaction.
_ = sess.Close()
}
return err
}
return txWithNoCheck(parentCtx, f)
}
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := f(newContext(parentCtx, sess, true)); err != nil {
return err
}
return sess.Commit()
}
// Insert inserts records into database
func Insert(ctx context.Context, beans ...any) error {
_, err := GetEngine(ctx).Insert(beans...)
return err
}
// Exec executes a sql with args
func Exec(ctx context.Context, sqlAndArgs ...any) (sql.Result, error) {
return GetEngine(ctx).Exec(sqlAndArgs...)
}
func Get[T any](ctx context.Context, cond builder.Cond) (object *T, exist bool, err error) {
if !cond.IsValid() {
panic("cond is invalid in db.Get(ctx, cond). This should not be possible.")
}
var bean T
has, err := GetEngine(ctx).Where(cond).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func GetByID[T any](ctx context.Context, id int64) (object *T, exist bool, err error) {
var bean T
has, err := GetEngine(ctx).ID(id).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
if !cond.IsValid() {
panic("cond is invalid in db.Exist(ctx, cond). This should not be possible.")
}
var bean T
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
}
func ExistByID[T any](ctx context.Context, id int64) (bool, error) {
var bean T
return GetEngine(ctx).ID(id).NoAutoCondition().Exist(&bean)
}
// DeleteByID deletes the given bean with the given ID
func DeleteByID[T any](ctx context.Context, id int64) (int64, error) {
var bean T
return GetEngine(ctx).ID(id).NoAutoCondition().NoAutoTime().Delete(&bean)
}
func DeleteByIDs[T any](ctx context.Context, ids ...int64) error {
if len(ids) == 0 {
return nil
}
var bean T
_, err := GetEngine(ctx).In("id", ids).NoAutoCondition().NoAutoTime().Delete(&bean)
return err
}
func Delete[T any](ctx context.Context, opts FindOptions) (int64, error) {
if opts == nil || !opts.ToConds().IsValid() {
panic("opts are empty or invalid in db.Delete(ctx, opts). This should not be possible.")
}
var bean T
return GetEngine(ctx).Where(opts.ToConds()).NoAutoCondition().NoAutoTime().Delete(&bean)
}
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
func DeleteByBean(ctx context.Context, bean any) (int64, error) {
return GetEngine(ctx).Delete(bean)
}
// FindIDs finds the IDs for the given table name satisfying the given condition
// By passing a different value than "id" for "idCol", you can query for foreign IDs, i.e. the repo IDs which satisfy the condition
func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([]int64, error) {
ids := make([]int64, 0, 10)
if err := GetEngine(ctx).Table(tableName).
Cols(idCol).
Where(cond).
Find(&ids); err != nil {
return nil, err
}
return ids, nil
}
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
// Timestamps of the entities won't be updated
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
if len(ids) == 0 {
return nil
}
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
return err
}
// DeleteBeans deletes all given beans, beans must contain delete conditions.
func DeleteBeans(ctx context.Context, beans ...any) (err error) {
e := GetEngine(ctx)
for i := range beans {
if _, err = e.Delete(beans[i]); err != nil {
return err
}
}
return nil
}
// TruncateBeans deletes all given beans, beans may contain delete conditions.
func TruncateBeans(ctx context.Context, beans ...any) (err error) {
e := GetEngine(ctx)
for i := range beans {
if _, err = e.Truncate(beans[i]); err != nil {
return err
}
}
return nil
}
// CountByBean counts the number of database records according non-empty fields of the bean as conditions.
func CountByBean(ctx context.Context, bean any) (int64, error) {
return GetEngine(ctx).Count(bean)
}
// TableName returns the table name according a bean object
func TableName(bean any) string {
return x.TableName(bean)
}
// InTransaction returns true if the engine is in a transaction otherwise return false
func InTransaction(ctx context.Context) bool {
_, ok := inTransaction(ctx)
return ok
}
func inTransaction(ctx context.Context) (*xorm.Session, bool) {
e := getEngine(ctx)
if e == nil {
return nil, false
}
switch t := e.(type) {
case *xorm.Engine:
return nil, false
case *xorm.Session:
if t.IsInTx() {
return t, true
}
return nil, false
default:
return nil, false
}
}

View file

@ -0,0 +1,128 @@
"""Reconstruct interrupted public source tests without touching owner checkouts."""
from pathlib import Path
import argparse
import hashlib
import io
import json
import shutil
import subprocess
import tarfile
import tempfile
import time
HERE = Path(__file__).resolve().parent
PACKET = HERE
OWNER = Path('/home/worsch/access-engine')
REVISION = 'd328a2601095686f74376d87791bff01e2a3a2a3'
TOOLCHAIN = Path('/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64')
MODULES = Path('/home/worsch/go/pkg/mod')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('target', choices=['adapter', 'producer', 'producer-all', 'producer-race'])
parser.add_argument('--attempt', type=int, default=1)
args = parser.parse_args()
suffix = '' if args.attempt == 1 else '-attempt' + str(args.attempt)
result_path = HERE / (args.target + '-result' + suffix + '.json')
log_path = HERE / (args.target + '-test' + suffix + '.jsonl')
if result_path.exists() or log_path.exists():
raise SystemExit('Refusing to overwrite an earlier attempt')
work = Path(tempfile.mkdtemp(prefix='custodian-resume-' + args.target + '-'))
source = work / 'source'
overlay = PACKET / 'access-engine-source'
candidate_pins = {str(f.relative_to(overlay)): hashlib.sha256(f.read_bytes()).hexdigest()
for f in sorted(overlay.rglob('*')) if f.is_file()}
pins = json.loads((overlay / 'adapters/go/decisionpep/producer-source.json').read_text())
for name, digest in pins['exact_unchanged_files'].items():
raw = (overlay / 'adapters/go/decisionpep' / name).read_bytes()
assert hashlib.sha256(raw).hexdigest() == digest, name
assert raw == subprocess.check_output(['git', '-C', str(OWNER), 'show', REVISION + ':' + name]), name
if args.target == 'adapter':
shutil.copytree(overlay / 'adapters/go/decisionpep', source)
test = ['go', 'test', '-p', '4', '-json', './...', '-count=1']
else:
source.mkdir()
archive = subprocess.check_output(['git', '-C', str(OWNER), 'archive', REVISION])
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
tar.extractall(source, filter='data')
for preimage in (PACKET / 'preimages/access-engine').rglob('*'):
if preimage.is_file():
assert preimage.read_bytes() == (source / preimage.relative_to(PACKET / 'preimages/access-engine')).read_bytes()
for item in overlay.rglob('*'):
if item.is_file():
target = source / item.relative_to(overlay)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(item, target)
# Test the exact producer-owned adapter in the real producer module.
for item in (overlay / 'adapters/go/decisionpep').rglob('*.go'):
target = source / item.relative_to(overlay / 'adapters/go/decisionpep')
target.parent.mkdir(parents=True, exist_ok=True)
if target.exists():
assert target.read_bytes() == item.read_bytes()
else:
shutil.copyfile(item, target)
test = ['go', 'test', '-p', '4', '-json', './cmd/access-engine', '-run',
'TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals', '-count=1']
if args.target in ['producer-all', 'producer-race']:
test = ['go', 'test', '-p', '4', '-json', './...', '-count=1']
if args.target == 'producer-race':
test.insert(2, '-race')
(work / 'home').mkdir()
fixture_binding = []
fixture_pins = {}
if args.target in ['producer-all', 'producer-race']:
# Existing full-suite tests execute real sibling producer generators.
# Supply committed public source and only the required package code;
# never mount a host HOME, full virtualenv, custody file or credential.
fixture = work / 'public-fixtures'
secret_source = fixture / 'secrets-engine'
secret_source.mkdir(parents=True)
secret_revision = subprocess.check_output(['git', '-C', '/home/worsch/secrets-engine', 'rev-parse', 'HEAD'], text=True).strip()
archive = subprocess.check_output(['git', '-C', '/home/worsch/secrets-engine', 'archive', secret_revision, 'src'])
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
tar.extractall(secret_source, filter='data')
(secret_source / '.venv/bin').mkdir(parents=True)
(secret_source / '.venv/bin/python').symlink_to('/usr/bin/python3')
shutil.copytree('/home/worsch/secrets-engine/.venv/lib/python3.12/site-packages/yaml', fixture / 'python/yaml', ignore=shutil.ignore_patterns('__pycache__'))
registry = subprocess.check_output(['git', '-C', '/home/worsch/informed-decision', 'show', 'HEAD:deploy/intake-submitters.json'])
registry_file = work / 'home/informed-decision/deploy/intake-submitters.json'
registry_file.parent.mkdir(parents=True)
registry_file.write_bytes(registry)
fixture_pins = {str(f.relative_to(fixture)): hashlib.sha256(f.read_bytes()).hexdigest()
for f in sorted(fixture.rglob('*')) if f.is_file() and not f.is_symlink()}
fixture_pins['intake-submitters.json'] = hashlib.sha256(registry).hexdigest()
fixture_pins['secrets_source_revision'] = secret_revision
fixture_binding = ['--ro-bind', str(secret_source), '/home/worsch/secrets-engine']
argv = ['bwrap', '--unshare-net', '--unshare-pid', '--die-with-parent', '--new-session']
for name in ['/usr', '/lib', '/lib64']:
argv += ['--ro-bind', name, name]
argv += ['--symlink', 'usr/bin', '/bin', '--proc', '/proc', '--dev', '/dev',
'--tmpfs', '/tmp', '--bind', str(work), '/work',
'--ro-bind', str(TOOLCHAIN), '/toolchain', '--ro-bind', str(MODULES), '/modules',
'--chdir', '/work/source', '--clearenv']
argv += fixture_binding
environment = {'PATH': '/toolchain/bin:/usr/bin:/bin', 'HOME': '/work/home',
'GOTOOLCHAIN': 'local', 'GOPROXY': 'off', 'GOSUMDB': 'off',
'GOMODCACHE': '/modules', 'GOCACHE': '/work/gocache',
'GOMAXPROCS': '4', 'CGO_ENABLED': '1' if args.target == 'producer-race' else '0'}
if fixture_binding:
environment['PYTHONPATH'] = '/work/public-fixtures/python'
for key, value in environment.items():
argv += ['--setenv', key, value]
argv += ['--'] + test
start = time.monotonic()
with log_path.open('xb') as log:
run = subprocess.run(argv, stdout=log, stderr=subprocess.STDOUT, timeout=900)
result = {'target': args.target, 'source_revision': REVISION, 'returncode': run.returncode,
'seconds': round(time.monotonic() - start, 2), 'argv': argv,
'workspace': str(work), 'network': 'disabled', 'pid_namespace': 'private',
'real_credentials': False, 'canonical_owner_modified': False,
'candidate_files': candidate_pins, 'public_fixture_pins': fixture_pins}
result_path.write_text(json.dumps(result, indent=2) + '\n')
print(json.dumps({'target': args.target, 'returncode': run.returncode, 'seconds': result['seconds']}))
raise SystemExit(run.returncode)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,32 @@
"""Reproduce shape counterexamples and their correction in the actual producer."""
from pathlib import Path
import hashlib
import json
import shutil
import subprocess
import sys
import time
HERE = Path(__file__).resolve().parent
label = sys.argv[1]
assert label in ['shapes-before', 'shapes-after']
prior = json.loads((HERE / 'producer-all-result.json').read_text())
assert prior['returncode'] != 0 # Completed initial full run with missing fixtures.
source = Path(prior['workspace']) / 'source'
overlay = HERE / 'access-engine-source'
pins = {}
for item in overlay.rglob('*'):
if item.is_file():
target = source / item.relative_to(overlay)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(item, target)
pins[str(item.relative_to(overlay))] = hashlib.sha256(item.read_bytes()).hexdigest()
argv = prior['argv'][:prior['argv'].index('--') + 1] + ['go', 'test', '-p', '4', '-json', './cmd/access-engine', '-run', '^TestForgejoActualPolicySignedLosslessAndLegacy$', '-count=1']
log = HERE / (label + '.jsonl')
assert not log.exists()
start = time.monotonic()
with log.open('xb') as output:
run = subprocess.run(argv, stdout=output, stderr=subprocess.STDOUT, timeout=180)
(HERE / (label + '.json')).write_text(json.dumps({'returncode': run.returncode, 'seconds': round(time.monotonic() - start, 2), 'argv': argv, 'source_pins': pins, 'network': 'disabled', 'pid_namespace': 'private'}, indent=2) + '\n')
print(json.dumps({'phase': label, 'returncode': run.returncode}))
raise SystemExit(run.returncode)

View file

@ -0,0 +1,64 @@
{"Time":"2026-10-17T23:02:42.636996993Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
{"Time":"2026-10-17T23:02:42.646026443Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
{"Time":"2026-10-17T23:02:42.64607436Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
{"Time":"2026-10-17T23:02:42.687480707Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
{"Time":"2026-10-17T23:02:42.687522808Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
{"Time":"2026-10-17T23:02:42.698710773Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.698759565Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.698777257Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
{"Time":"2026-10-17T23:02:42.698784318Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
{"Time":"2026-10-17T23:02:42.709737369Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.709802834Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.709814349Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
{"Time":"2026-10-17T23:02:42.709821274Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
{"Time":"2026-10-17T23:02:42.719256293Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.719301379Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.719310919Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups"}
{"Time":"2026-10-17T23:02:42.719316573Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups\n"}
{"Time":"2026-10-17T23:02:42.72680925Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.726880585Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.726891929Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
{"Time":"2026-10-17T23:02:42.726898106Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
{"Time":"2026-10-17T23:02:42.735786052Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.735825124Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.735834159Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
{"Time":"2026-10-17T23:02:42.735838559Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
{"Time":"2026-10-17T23:02:42.743414049Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.743461958Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.743471634Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
{"Time":"2026-10-17T23:02:42.743476873Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
{"Time":"2026-10-17T23:02:42.752150093Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.752196875Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.752208281Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups"}
{"Time":"2026-10-17T23:02:42.752215351Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups\n"}
{"Time":"2026-10-17T23:02:42.760682007Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.76073492Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.76074391Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
{"Time":"2026-10-17T23:02:42.760747995Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
{"Time":"2026-10-17T23:02:42.770777151Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.770835709Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.770846935Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
{"Time":"2026-10-17T23:02:42.770852427Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
{"Time":"2026-10-17T23:02:42.777920512Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.777985842Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.778012271Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
{"Time":"2026-10-17T23:02:42.778026735Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
{"Time":"2026-10-17T23:02:42.786891809Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.786932116Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.786939682Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
{"Time":"2026-10-17T23:02:42.78694383Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
{"Time":"2026-10-17T23:02:42.795039209Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.795082023Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.795104494Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
{"Time":"2026-10-17T23:02:42.79510893Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
{"Time":"2026-10-17T23:02:42.80195675Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.802007698Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.80201832Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
{"Time":"2026-10-17T23:02:42.80202805Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
{"Time":"2026-10-17T23:02:42.811920714Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.01s)\n"}
{"Time":"2026-10-17T23:02:42.811968316Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.01}
{"Time":"2026-10-17T23:02:42.828555999Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy (0.18s)\n"}
{"Time":"2026-10-17T23:02:42.828606441Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.18}
{"Time":"2026-10-17T23:02:42.828622194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"PASS\n"}
{"Time":"2026-10-17T23:02:42.832859699Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"ok \tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.195s\n"}
{"Time":"2026-10-17T23:02:42.832918438Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.196}

View file

@ -0,0 +1,66 @@
{"Time":"2026-10-17T23:01:47.490515679Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
{"Time":"2026-10-17T23:01:47.528993671Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
{"Time":"2026-10-17T23:01:47.529062716Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
{"Time":"2026-10-17T23:01:47.678493323Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
{"Time":"2026-10-17T23:01:47.678565894Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
{"Time":"2026-10-17T23:01:47.714806242Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.03s)\n"}
{"Time":"2026-10-17T23:01:47.714959986Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.03}
{"Time":"2026-10-17T23:01:47.714984675Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
{"Time":"2026-10-17T23:01:47.714994666Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
{"Time":"2026-10-17T23:01:47.744208882Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.03s)\n"}
{"Time":"2026-10-17T23:01:47.744291778Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.03}
{"Time":"2026-10-17T23:01:47.744315484Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
{"Time":"2026-10-17T23:01:47.744324077Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
{"Time":"2026-10-17T23:01:47.767281983Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.03s)\n"}
{"Time":"2026-10-17T23:01:47.768215215Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.03}
{"Time":"2026-10-17T23:01:47.768287533Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
{"Time":"2026-10-17T23:01:47.768298606Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
{"Time":"2026-10-17T23:01:47.786986918Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.02s)\n"}
{"Time":"2026-10-17T23:01:47.787157389Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.02}
{"Time":"2026-10-17T23:01:47.787183981Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
{"Time":"2026-10-17T23:01:47.787194414Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
{"Time":"2026-10-17T23:01:47.828058556Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.04s)\n"}
{"Time":"2026-10-17T23:01:47.828131479Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.04}
{"Time":"2026-10-17T23:01:47.828155798Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
{"Time":"2026-10-17T23:01:47.828167241Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
{"Time":"2026-10-17T23:01:47.854622614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.03s)\n"}
{"Time":"2026-10-17T23:01:47.854699568Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.03}
{"Time":"2026-10-17T23:01:47.854717521Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
{"Time":"2026-10-17T23:01:47.854727025Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
{"Time":"2026-10-17T23:01:47.873861799Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.02s)\n"}
{"Time":"2026-10-17T23:01:47.873927661Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.02}
{"Time":"2026-10-17T23:01:47.873944081Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
{"Time":"2026-10-17T23:01:47.873952891Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
{"Time":"2026-10-17T23:01:47.947872691Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.07s)\n"}
{"Time":"2026-10-17T23:01:47.948100493Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.07}
{"Time":"2026-10-17T23:01:47.948174282Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups"}
{"Time":"2026-10-17T23:01:47.948187591Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups\n"}
{"Time":"2026-10-17T23:01:47.991945801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":" forgejo_application_profile_test.go:122: actual PDP allowed adverse input\n"}
{"Time":"2026-10-17T23:01:47.992029499Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups (0.04s)\n"}
{"Time":"2026-10-17T23:01:47.992047624Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Elapsed":0.04}
{"Time":"2026-10-17T23:01:47.992061285Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
{"Time":"2026-10-17T23:01:47.992070131Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
{"Time":"2026-10-17T23:01:48.03916284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.05s)\n"}
{"Time":"2026-10-17T23:01:48.039228305Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.05}
{"Time":"2026-10-17T23:01:48.039246312Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
{"Time":"2026-10-17T23:01:48.039256285Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
{"Time":"2026-10-17T23:01:48.075858483Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.04s)\n"}
{"Time":"2026-10-17T23:01:48.075935743Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.04}
{"Time":"2026-10-17T23:01:48.075951325Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups"}
{"Time":"2026-10-17T23:01:48.07595935Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups\n"}
{"Time":"2026-10-17T23:01:48.114404291Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":" forgejo_application_profile_test.go:122: actual PDP allowed adverse input\n"}
{"Time":"2026-10-17T23:01:48.116214261Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups (0.04s)\n"}
{"Time":"2026-10-17T23:01:48.116275632Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Elapsed":0.04}
{"Time":"2026-10-17T23:01:48.116289032Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
{"Time":"2026-10-17T23:01:48.116297607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
{"Time":"2026-10-17T23:01:48.163857554Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.05s)\n"}
{"Time":"2026-10-17T23:01:48.163928177Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.05}
{"Time":"2026-10-17T23:01:48.163944147Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
{"Time":"2026-10-17T23:01:48.163952876Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
{"Time":"2026-10-17T23:01:48.181178835Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.02s)\n"}
{"Time":"2026-10-17T23:01:48.181243633Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.02}
{"Time":"2026-10-17T23:01:48.237903183Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy (0.71s)\n"}
{"Time":"2026-10-17T23:01:48.238185189Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.71}
{"Time":"2026-10-17T23:01:48.238415877Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\n"}
{"Time":"2026-10-17T23:01:48.246822218Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.755s\n"}
{"Time":"2026-10-17T23:01:48.247040589Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.757}

View file

@ -0,0 +1,99 @@
import pathlib,subprocess,json,sqlite3,os,shlex,time
w=pathlib.Path('/tmp/custodian-forgejo-successor-attempt2/cli-work');s='/tmp/forgejo-private-input-r1';r='/tmp/forgejo-independent-r1'
fixtures=w/'fixtures'; fixtures.mkdir(exist_ok=True)
A='AbCdEf012345678-'*4;B='ZyXwVu_987654321'*4 # synthetic exact64
assert len(A)==64 and len(B)==64
for name,data,mode in [('good',A,0o400),('replacement',B,0o440),('newline',A+'\n',0o400),('short',A[:-1],0o400),('long',A+'A',0o400),('badbyte',A[:-1]+'=',0o400),('world',A,0o444),('writable',A,0o600),('executable',A,0o500),('empty','',0o400),('CLIENT_SECRET','',0o400)]:
p=fixtures/name
if p.exists():p.chmod(0o600)
p.write_text(data);p.chmod(mode)
for name in ['symlink']:
p=fixtures/name
if not p.exists():p.symlink_to('good')
if not (fixtures/'fifo').exists():os.mkfifo(fixtures/'fifo',0o400)
(fixtures/'directory').mkdir(exist_ok=True)
(fixtures/'badparent').mkdir(exist_ok=True);(fixtures/'badparent').chmod(0o777)
if (fixtures/'badparent/value').exists():(fixtures/'badparent/value').chmod(0o600)
(fixtures/'badparent/value').write_text(A);(fixtures/'badparent/value').chmod(0o400)
if not (fixtures/'parentlink').exists():(fixtures/'parentlink').symlink_to('directory',target_is_directory=True)
(w/'runtime').mkdir(exist_ok=True)
dbfile=w/'runtime/test.db'
if dbfile.exists():dbfile.unlink() # isolated synthetic test database only
ini=w/'runtime/app.ini';ini.write_text('''APP_NAME = synthetic-private-input-test
RUN_USER = worsch
WORK_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime
[security]
INSTALL_LOCK = true
SECRET_KEY = SYNTHETIC-TEST-KEY
[database]
DB_TYPE = sqlite3
SLOW_QUERY_THRESHOLD = 1ns
PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/test.db
[server]
APP_DATA_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/data
OFFLINE_MODE = true
[repository]
ROOT = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/repos
[log]
MODE = console
LEVEL = Error
''')
env={'PATH':'/usr/bin:/bin','HOME':str(w/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':str(w/'home/test.gitconfig'),'GIT_TERMINAL_PROMPT':'0','USER':'worsch','GITEA_WORK_DIR':str(w/'runtime')}
base=['bwrap','--unshare-pid','--unshare-net','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib','--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev','--tmpfs','/tmp','--bind',str(w),str(w),'--dir','/run','--ro-bind',str(fixtures),'/run/forgejo-oidc','--ro-bind',str(fixtures/'good'),'/run/forgejo-oidc/CLIENT_SECRET','--bind',str(fixtures),'/run/writable-input','--chdir',str(w/'runtime'),'--','env','-i']+[k+'='+v for k,v in env.items()]+[str(w/'forgejo'),'--config',str(ini)]
results=[]
def run(args,ok=True,reason=None):
p=subprocess.run(base+args,capture_output=True,text=True,timeout=60); output=p.stdout+p.stderr
assert A not in output and B not in output, 'synthetic input leaked in output'
assert all(A not in arg and B not in arg for arg in base+args), 'synthetic input in argv'
assert all(A not in v and B not in v for v in env.values()), 'synthetic input in env'
results.append({'operation':args,'returncode':p.returncode,'stdout_stderr_secret_absent':True})
(w/'cli-attempts.json').write_text(json.dumps(results,indent=2))
if (p.returncode==0)!=ok:raise AssertionError('CLI unexpected status: '+repr(args)+'\n'+output[-5000:])
if reason:assert reason in output,output[-2000:]
return p
run(['migrate'])
clean_ini=ini.read_text()
ini.write_text(clean_ini.replace('DB_TYPE = sqlite3', 'DB_TYPE = sqlite3\nLOG_SQL = true'))
run(['admin','auth','add-oauth','--name','refused-debug-logging','--provider','github','--secret-file','/run/forgejo-oidc/CLIENT_SECRET'],False,'OAuth configuration requires SQL logging disabled')
ini.write_text(clean_ini)
def rows():
with sqlite3.connect(w/'runtime/test.db') as db:return db.execute('select id,name,cfg from login_source order by id').fetchall()
def cfg():return json.loads(rows()[0][2])
run(['admin','auth','add-oauth','--name','synthetic-oauth','--provider','github','--key','synthetic-public-id','--secret-file','/run/forgejo-oidc/CLIENT_SECRET','--group-claim-name','groups','--admin-group','unchanged-admin','--scopes','read:user,user:email'])
assert len(rows())==1 and cfg()['ClientSecret']==A
id=str(rows()[0][0]); before=cfg()
run(['admin','auth','update-oauth','--id',id,'--icon-url','https://synthetic.invalid/icon'])
assert cfg()['ClientSecret']==A
preserved=cfg()
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'])
after=cfg();assert after['ClientSecret']==B
assert {k:v for k,v in after.items() if k!='ClientSecret'}=={k:v for k,v in preserved.items() if k!='ClientSecret'},'secret replacement changed unrelated config'
# Guarded inactive source exercises actual update omission and strict list semantics
# without discovery/network or claiming a browser login.
with sqlite3.connect(w/'runtime/test.db') as db:
guarded=after|{'Provider':'openidConnect','OpenIDConnectAutoDiscoveryURL':'https://synthetic.invalid/.well-known/openid-configuration','RequiredClaimName':'groups','RequiredClaimValues':['forge-admins','forge-coulomb-owners','forge-coulomb-readers']}
db.execute('update login_source set is_active=0,cfg=? where id=?',(json.dumps(guarded),id))
run(['admin','auth','update-oauth','--id',id,'--name','synthetic-guarded'])
assert cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] and cfg()['ClientSecret']==B
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/good'])
assert cfg()['ClientSecret']==A and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
for path in ['/run/forgejo-oidc/'+n for n in ['newline','short','long','badbyte','world','writable','executable','empty','symlink','fifo','directory','missing','badparent/value','parentlink/value']]+['relative','','/run/forgejo-oidc/../forgejo-oidc/good','/run/writable-input/good']:
before=rows()
for cmd in [['admin','auth','add-oauth','--name','refused','--provider','github'],['admin','auth','update-oauth','--id',id,'--name','refused']]:
run(cmd+['--secret-file',path],False,'secret-file input refused')
assert rows()==before,'failure changed DB'
for action in [['add-oauth','--name','refused'],['update-oauth','--id',id]]:
before=rows();run(['admin','auth']+action+['--secret','','--secret-file','/run/forgejo-oidc/good'],False,'mutually exclusive');assert rows()==before
# Failure to clear/ambiguously replace gate keeps stored application bytes unchanged.
before=rows();run(['admin','auth','update-oauth','--id',id,'--required-claim-values',''],False);assert rows()==before
# Actual XORM error hook under a failing SQLite trigger must not disclose cfg.
before=rows()
with sqlite3.connect(w/'runtime/test.db') as database:
database.execute("create trigger synthetic_private_error before update on login_source begin select raise(abort, 'synthetic-db-failure'); end")
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'],False,'OAuth source update failed')
assert rows()==before
with sqlite3.connect(w/'runtime/test.db') as database:database.execute('drop trigger synthetic_private_error')
# Legacy explicit secret remains supported; synthetic legacy string is not private input.
run(['admin','auth','update-oauth','--id',id,'--secret','synthetic-legacy-literal'])
assert cfg()['ClientSecret']=='synthetic-legacy-literal' and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
(w/'cli-results.json').write_text(json.dumps({'passed':True,'subprocesses':len(results),'real_backend_cli_and_sqlite':True,'network':'disabled by bwrap','secret_inputs':'synthetic only','values_never_in_argv_env_output':True,'actual_browser_login':False,'actual_readonly_file_bind':True,'replacement_preserves_other_config':True,'unsafe_SQL_logging_refused':True,'actual_error_and_slow_hooks_no_leak':True},indent=2));print('CLI integrations passed:',len(results))