IBMa / equal-access

IBM Equal Access Accessibility Checker contains tools to automate accessibility checking from a browser or in a continuous development/build environment
Apache License 2.0
627 stars 81 forks source link

[BUG]: Does not respect engineMode: "REMOTE" or fetch/axios/NodeJS proxy/TLS certificate settings #1852

Closed tgrushka closed 5 months ago

tgrushka commented 6 months ago

Project

accessibility-checker for Node

Browser

Chrome

Operating system

MacOS, Other

Description

Error that led to this problem:

UNABLE_TO_VERIFY_LEAF_SIGNATURE when using a proxy server with self-signed TLS certificate, even when engineMode: "REMOTE" and passing in Playwright page to getCompliance(), because Accessibility Checker loads its config directly using fetch/axios outside this context.

Accessibility Checker (Engine) has a convoluted process for getting its config and retrieving checker rules. It forces use of its internal fetch_get_text and fetch_get_json functions which cannot be overridden to user's environment preferences.

For example:

It does not respect these environment variables either: NODE_OPTIONS=--use-openssl-ca NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt

These are working fine for me in my container environment in node-fetch package, and in Playwright. The only way I found to override was to override global.fetch as follows, which, in my case, has to use a page.evaluate to fetch from the browser's context, in which I have properly set up my proxy config, and which respects the machine's trusted certificate store.

My override, in case it helps someone else (that took hours trying to monkey-patch ACEngineManager and ACConfigManager, to no avail):

    global.fetch = async (url: string) => {
        // page is a Playwright page instance
        const text = await page.evaluate(async (url) => {
            return await (await fetch(url)).text()
        }, url)
        return {
            async json() {
                return JSON.parse(text)
            },
            async text() {
                return text
            }
        }
    }

Expected Behavior

If we set engineMode: "REMOTE" in .achecker.yml (would be nice to not need a file and be able to use await ACConfigManager.setConfig(config) in code instead, but that's another issue), Accessibility Checker should respect our settings and retrieve its dependencies inside whatever context we configured, i.e. Playwright, Puppeteer, etc., or allow us to override the configuration for proxy server and TLS certificates.

(NODE_TLS_REJECT_UNAUTHORIZED is a last resort, should not be required to do this, should not be set globally by the checker, and should be set on a per-request basis, or allow the user to override it in the config only for the checker and not globally. Further, it does not work for self-signed proxy certificates.)

Steps to reproduce

  1. Set up proxy server with self-signed CA certificate requiring HTTPS / SSL connection;
  2. Trust the proxy server's CA certificate in the local machine store such that curl -x, httpie --proxy, etc. trust the certificate and do not throw an unverified certificate error;
  3. Setup IBM Checker Node JS as per the usual instructions;
  4. Setup Playwright for testing in the NodeJS project, ensuring that Playwright either trusts the proxy's self-signed certificate or ignores TLS unverified certificates;
  5. Load up a Playwright page instance with a URL to test;
  6. Call await getCompliance(page) on the page;
  7. Note that other tests on the page work, but Accessibility Checker throws an UNABLE_TO_VERIFY_LEAF_SIGNATURE error.
tombrunet commented 6 months ago

The Node way to do this is via NODE_EXTRA_CA_CERTS (https://nodejs.org/api/cli.html#node_extra_ca_certsfile). You can specify an environment variable pointing to the cert that Node should recognize.

As for overriding where/how we fetch...

If you use engineMode: "REMOTE" it tells our checker to fetch ${config.rulePack}/ace.js directly into the browser. The fetch is handled by the browser within the page context, so things that you do in the Node environment don't affect this kind of fetch - it's executed with the page's javascript context, not the Node context.

The default setting is engineMode: "INJECT". This will fetch ${config.rulePack}/ace.js and then push it to the browser through the browser automation protocol (Puppeteer, Selenium, etc). You could pre-fetch ace.js and place it wherever you like and override with the rulePack configuration to fetch from some other location of your choosing.

I suspect NODE_EXTRA_CA_CERTS with the default INJECT setting is what you're really looking for.

tombrunet commented 5 months ago

Given no further follow-up, closing...