Files
computer-playbook/roles/web-app-wordpress/files/playwright/addons/infinito-http-onion-socks.spec.js
kevinveenbirkenbachandClaude Opus 5 3016388bd9 feat(wordpress): route server-side OIDC to an onion issuer through Tor
daggerhart-openid-connect-generic calls wp_remote_post() against
http://auth.<onion>/.../token, which bottoms out in libcurl -- and libcurl
refuses a .onion host at name resolution (RFC 7686) before /etc/hosts,
extra_hosts or DNS is consulted. The result is cURL error 6, which WordPress
turns into WP_Error('http_request_failed') and a bounce to
/wp-login.php?login-error=http_request_failed. Five specs of WordPress 0 in run
31340965955 failed on that single cause, 60 occurrences in the log.

Only a configured proxy makes curl skip the check, and socks5h:// hands the name
to Tor. The mu-plugin hooks http_api_curl because http_request_args cannot
express a proxy and WP core's WP_HTTP_Proxy hardcodes CURLPROXY_HTTP, so SOCKS5
is reachable only on the raw handle. env.j2 sets WORDPRESS_OIDC_SOCKS_PROXY only
when the issuer is an onion, so a clearnet deployment never routes through Tor.

Deliberately not adding the onion_oidc_socks include: 238b8b41f established it
is a no-op for this client, since libcurl never reads /etc/hosts here.

Depends on 1bb31fba4 -- without it swarm renders socks5h://host.docker.internal,
which is unreachable from inside the overlay.

The plugin's behaviour is unexercised: syntax checked, never run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 15:56:10 +02:00

51 lines
1.6 KiB
JavaScript

const { test, expect } = require("@playwright/test");
const { skipUnlessAddonEnabled } = require("../addon-gating");
const { skipUnlessServiceEnabled } = require("../service-gating");
const {
resolveTimeout,
isOnionTarget,
isSplitRealmOidc,
} = require("../timeouts");
const { gotoOnion } = require("../personas");
const shared = require("../_shared");
test("addon infinito-http-onion-socks: server-side OIDC against an onion issuer completes", async ({
browser,
}) => {
skipUnlessAddonEnabled("infinito-http-onion-socks");
skipUnlessServiceEnabled("sso");
test.skip(
!isOnionTarget() && !isSplitRealmOidc(),
"the SOCKS route only applies when the OIDC issuer is a .onion"
);
test.setTimeout(resolveTimeout(180_000));
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
await shared.wpAdminLoginViaOidc(
page,
shared.env.wpBaseUrl,
shared.env.adminUsername,
shared.env.adminPassword
);
await gotoOnion(
page,
`${shared.env.wpBaseUrl}/wp-admin/plugins.php?plugin_status=mustuse`,
{ waitUntil: "domcontentloaded", timeout: resolveTimeout(60_000) }
);
await expect(
page.locator("#the-list"),
"the Must-Use plugins screen must list the onion-SOCKS mu-plugin under the Plugin Name its header declares — one absent from this screen was never copied into wp-content/mu-plugins"
).toContainText("Infinito.Nexus HTTP Onion SOCKS", {
timeout: resolveTimeout(30_000),
});
} finally {
await page.close().catch(() => {});
await context.close().catch(() => {});
}
});