. "$BST_LIB" # BST-TIMEOUT: 600 # the interpreter is one large static image and this suite # # starts it once, importing requests, urllib3, idna, # # certifi and simplejson in that one start; on a loaded # # guest a single start has been minutes, and the runner's # # 60s default would report the port as broken when what # # ran out was the clock. # # python3-requests 2.32.4 (poky meta/recipes-devtools/python, `inherit pypi # python_setuptools_build_meta', carrying the recipe's CVE-2026-25645.patch, # with a meta-brintos bbappend that corrects RDEPENDS and does nothing else) on # wasm32-hwjs -- the HTTP client python3-sphinx names in its RDEPENDS. # # THE MODULE IMPORTS ON THIS IMAGE, which is what separates this port from the # sphinxcontrib-* ports beside it. requests' three unconditional third-party # imports -- urllib3, idna, certifi -- are all installed here, so `import # requests' is asserted directly, against the real module, and every case after # it is measured rather than stubbed. # # WHAT IS ACTUALLY BEING TESTED, given that no request can be sent. Three # layers, and each catches a different way a pure-Python port breaks: # # the tree -- the files the .deb installed, and the dpkg control stanza, # which is where the RDEPENDS correction is asserted; # the import -- that it happens at all, and WHICH third-party modules it # bound, since two of them (chardet vs charset_normalizer, # simplejson vs stdlib json) depend on what else this image # carries rather than on requests; # the objects -- PreparedRequest and Response, which are everything the # request path produces BEFORE and AFTER the socket. A port # that imported cleanly and had lost idna, or urllib3's # parse_url, would pass an import test and fail here. # # NO NETWORK, ANYWHERE. Nothing below opens a socket, so this port cannot go red # on somebody else's outage, and it closes on this suite: non-interactive, no # browser pass, no BRINTOS-780 follow-on. # # WHY PURENESS IS LOAD-BEARING. This image's python3 is one static binary: # lib-dynload is empty by construction and this arm has no dynamic loader at all # (BRINTOS-1476), and its Py* export surface is empty (BRINTOS-2046). A release # of requests that grew a compiled extension would install into site-packages # and die at `import requests'. The no-.so case asserts that negative directly, # and the empty lib-dynload beside it is the mechanism. # # THE RDEPENDS CORRECTION IS ASSERTED IN BOTH DIRECTIONS. poky declares # python3-netserver and python3-email, which requests imports nothing from, and # omits python3-netclient, python3-logging, python3-io, python3-crypt and # python3-datetime, which it imports at module scope. The bbappend fixes both. # The removals correct a declaration and reduce no image, and that is asserted # rather than argued: the Depends line must not name them, AND the image must # still carry python3-netserver, which python3-logging -- itself now declared -- # RDEPENDS on in poky's own manifest. P=python3-requests require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/requests DI=$SITE/requests-2.32.4.dist-info PKGLIST=/var/lib/dpkg/info/python3-requests.list PROBE=$BST_DATA/python3-requests-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-requests.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # --- the tree the package installs ------------------------------------------ # Asserted as files rather than as an import, so a red separates "not installed" # from "installed and does not work". check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/src-init test -s "$PKGDIR/__init__.py" check $P/src-version test -s "$PKGDIR/__version__.py" check $P/src-api test -s "$PKGDIR/api.py" check $P/src-sessions test -s "$PKGDIR/sessions.py" check $P/src-adapters test -s "$PKGDIR/adapters.py" check $P/src-models test -s "$PKGDIR/models.py" check $P/src-utils test -s "$PKGDIR/utils.py" check $P/src-compat test -s "$PKGDIR/compat.py" check $P/src-certs test -s "$PKGDIR/certs.py" check $P/src-cookies test -s "$PKGDIR/cookies.py" check $P/src-auth test -s "$PKGDIR/auth.py" check $P/src-exceptions test -s "$PKGDIR/exceptions.py" check $P/src-help test -s "$PKGDIR/help.py" check $P/src-packages test -s "$PKGDIR/packages.py" check $P/src-status-codes test -s "$PKGDIR/status_codes.py" check $P/src-structures test -s "$PKGDIR/structures.py" check $P/src-hooks test -s "$PKGDIR/hooks.py" check $P/pycache-init test -s "$PKGDIR/__pycache__/__init__.cpython-312.pyc" check $P/pycache-sessions test -s "$PKGDIR/__pycache__/sessions.cpython-312.pyc" # THE NO-.so ASSERTION, and the mechanism that makes it matter. Both `find' # cases would pass VACUOUSLY against a directory that is not there, so each is # preceded by the existence case that makes its emptiness mean something (the # package-dir case above serves the first). check_eq $P/no-so "" "$(find "$PKGDIR" -name '*.so' 2>/dev/null)" check $P/lib-dynload-dir test -d /usr/lib/python3.12/lib-dynload check_eq $P/lib-dynload-empty "" \ "$(find /usr/lib/python3.12/lib-dynload -name '*.so' 2>/dev/null)" # --- the shipped file list -------------------------------------------------- cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" check_eq $P/pkg-no-so "" "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # requests declares NO [project.scripts]: it is a library and ships no command. # A build that grew one would be shipping an entry point nobody described. check_eq $P/pkg-no-bin "" "$(grep -E '^/usr/(bin|sbin)/' "$W/pkgf" 2>/dev/null)" # Nothing outside the three kinds this package is made of. _bad= while IFS= read -r _f; do [ -f "$_f" ] || continue case "$_f" in *.py|*.pyc) continue ;; */requests-2.32.4.dist-info/LICENSE) continue ;; */requests-2.32.4.dist-info/METADATA) continue ;; */requests-2.32.4.dist-info/RECORD) continue ;; */requests-2.32.4.dist-info/WHEEL) continue ;; */requests-2.32.4.dist-info/top_level.txt) continue ;; esac _bad="$_bad $_f" done < "$W/pkgf" check_eq $P/pkg-only-expected "" "$_bad" # --- dist-info -------------------------------------------------------------- check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -s "$DI/METADATA" check $P/dist-info-record test -s "$DI/RECORD" check $P/dist-info-wheel test -s "$DI/WHEEL" check $P/no-entry-points test ! -f "$DI/entry_points.txt" check_out $P/dist-info-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" check_out $P/dist-info-tag-py3 "Tag: py3-none-any" cat "$DI/WHEEL" check_out $P/dist-info-name "Name: requests" cat "$DI/METADATA" check_out $P/dist-info-version "Version: 2.32.4" cat "$DI/METADATA" # UPSTREAM'S OWN DECLARATION, pinned because the bbappend's reasoning rests on # it. charset_normalizer, idna, urllib3 and certifi are UNCONDITIONAL; PySocks # and chardet are EXTRAS. poky substitutes chardet for charset_normalizer, which # is correct for this image and is what `probe-chardet-bound' below measures. # The spelling is THIS IMAGE'S setuptools normalisation, not the PyPI wheel's: # a space before the specifier, `charset-normalizer' hyphenated, and the extra # single-quoted. A setuptools bump can re-normalise all six without the # declaration changing at all -- read METADATA before suspecting the recipe. check_out $P/meta-requires-urllib3 "Requires-Dist: urllib3 <3,>=1.21.1" cat "$DI/METADATA" check_out $P/meta-requires-idna "Requires-Dist: idna <4,>=2.5" cat "$DI/METADATA" check_out $P/meta-requires-certifi "Requires-Dist: certifi >=2017.4.17" cat "$DI/METADATA" check_out $P/meta-requires-charsetn "Requires-Dist: charset-normalizer <4,>=2" cat "$DI/METADATA" check_out $P/meta-socks-extra \ "Requires-Dist: PySocks !=1.5.7,>=1.5.6 ; extra == 'socks'" cat "$DI/METADATA" check_out $P/meta-chardet-extra \ "Requires-Dist: chardet <6,>=3.0.2 ; extra == 'use_chardet_on_py3'" cat "$DI/METADATA" # --- dpkg: WHERE THE RDEPENDS CORRECTION IS ASSERTED ------------------------ sed -n '/^Package: python3-requests$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $P/pkg-known test -s "$W/st" check_out $P/pkg-installed "Status: install ok installed" cat "$W/st" check_out $P/pkg-version "Version: 2.32.4-r0" cat "$W/st" check_out $P/pkg-arch "Architecture: wasm32" cat "$W/st" grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" check_out $P/pkg-depends-core "python3-core" cat "$W/dep" # The five the bbappend ADDS, each a module-scope import poky did not declare. # Without python3-netclient a minimal image installs requests and dies at # `from http import cookiejar' -- the case that made this correction necessary. check_out $P/pkg-depends-netclient "python3-netclient" cat "$W/dep" check_out $P/pkg-depends-logging "python3-logging" cat "$W/dep" check_out $P/pkg-depends-io "python3-io" cat "$W/dep" check_out $P/pkg-depends-crypt "python3-crypt" cat "$W/dep" check_out $P/pkg-depends-datetime "python3-datetime" cat "$W/dep" # The four poky declares that ARE real: three third-party and one stdlib group. check_out $P/pkg-depends-urllib3 "python3-urllib3" cat "$W/dep" check_out $P/pkg-depends-certifi "python3-certifi" cat "$W/dep" check_out $P/pkg-depends-idna "python3-idna" cat "$W/dep" check_out $P/pkg-depends-json "python3-json" cat "$W/dep" check_out $P/pkg-depends-compression "python3-compression" cat "$W/dep" # Left declared deliberately: optional imports this image satisfies. See header. check_out $P/pkg-depends-chardet "python3-chardet" cat "$W/dep" check_out $P/pkg-depends-pysocks "python3-pysocks" cat "$W/dep" # The two the bbappend REMOVES, asserted as absences. check_eq $P/pkg-depends-no-netserver "" \ "$(grep -E 'python3-netserver([ ,]|$)' "$W/dep" 2>/dev/null)" check_eq $P/pkg-depends-no-email "" \ "$(grep -E 'python3-email([ ,]|$)' "$W/dep" 2>/dev/null)" # THE OTHER HALF OF THAT CLAIM: removing them corrected a declaration and # reduced no image. python3-logging RDEPENDS on python3-netserver in poky's own # manifest, and python3-netclient on python3-email, so both are still installed. # If either of these goes red the removal DID cost the image something and the # bbappend is wrong. sed -n '/^Package: python3-netserver$/,/^$/p' /var/lib/dpkg/status > "$W/ns" 2>/dev/null sed -n '/^Package: python3-email$/,/^$/p' /var/lib/dpkg/status > "$W/em" 2>/dev/null check_out $P/image-still-has-netserver "Status: install ok installed" cat "$W/ns" check_out $P/image-still-has-email "Status: install ok installed" cat "$W/em" # python3-cryptography CANNOT BE BUILT ON THIS ARM -- rust has no wasm32 target. # Its absence is what makes the pyopenssl SNI fallback in __init__.py dead code, # and `probe-ssl-has-sni' below is the condition that keeps it unreachable. A # tripwire: this goes red on the day cryptography is ported, which is exactly # when that branch needs looking at again. check_eq $P/cryptography-absent "" \ "$(sed -n '/^Package: python3-cryptography$/p' /var/lib/dpkg/status 2>/dev/null)" check_eq $P/no-dev-package "" \ "$(sed -n '/^Package: python3-requests-dev$/p' /var/lib/dpkg/status 2>/dev/null)" check_eq $P/no-dbg-package "" \ "$(sed -n '/^Package: python3-requests-dbg$/p' /var/lib/dpkg/status 2>/dev/null)" # --- CVE-2026-25645, asserted in the source before it is asserted in behaviour - # The recipe carries the backport of upstream 66d21cb0. The vulnerable code # joined tempfile.gettempdir() with the member's own basename and REUSED an # existing file; the fix uses tempfile.mkstemp. Both halves are asserted here -- # the call that must be present and the join that must be gone -- so a rebuild # that dropped the patch names itself rather than only failing the behavioural # case further down. check_out $P/cve-fix-mkstemp "tempfile.mkstemp(suffix=suffix)" cat "$PKGDIR/utils.py" check_eq $P/cve-fix-no-gettempdir "" \ "$(grep -n 'tmp = tempfile.gettempdir()' "$PKGDIR/utils.py" 2>/dev/null)" # --- the probe run ---------------------------------------------------------- # Forked, so a failure to stage the exec costs this file its cases rather than # the run its terminator. One interpreter start-up answers every question below. PROBE_OUT=$W/probe.out ( python3 "$PROBE" > "$PROBE_OUT" 2>&1 ) # field : the value the probe printed for , empty if it never got # there. Empty is a red against every expectation below, which is the intent -- # a probe that died half-way must not leave the cases it never reached looking # untested. field() { sed -n "s/^$1 //p" "$PROBE_OUT" | head -1; } # THE IMPORT ITSELF, and it is the case every other one below depends on. check_eq $P/import "ok" "$(field bare-import)" # The probe ran to the end. This is what tells a reader WHY forty cases went red # at once, when they do. check_eq $P/probe-complete "yes" "$(field probe-complete)" # --- identity --------------------------------------------------------------- # The version the recipe pins, from the interpreter's own side, and the module # resolved to the path the recipe installed -- `import requests' succeeding from # a stray copy elsewhere would satisfy every case below while the port had # installed nothing. check_eq $P/version "2.32.4" "$(field version)" check_eq $P/build "0x23204" "$(field build)" check_eq $P/title "requests" "$(field title)" check_eq $P/license "Apache-2.0" "$(field license)" check_eq $P/import-path "$PKGDIR/__init__.py" "$(field module-file)" # requests defines NO __all__ -- measured on the built payload, not assumed, and # the first replay of the probe is what said so. What it exports is whatever # __init__.py bound, so the names are asserted against the module itself. check_eq $P/no-dunder-all "yes" "$(field no-dunder-all)" check_eq $P/public-names \ "get,post,put,patch,delete,head,options,request,session,Session,Response,Request,PreparedRequest,codes,adapters,auth,cookies,exceptions,models,sessions,structures,utils" \ "$(field public-names)" # --- the import closure, measured ------------------------------------------- # These are the values the bbappend's RDEPENDS correction was derived from, so # they are measured rather than trusted. check_eq $P/urllib3-version "2.2.2" "$(field urllib3-version)" check_eq $P/is-urllib3-1 "False" "$(field is-urllib3-1)" check_eq $P/idna-version "3.7" "$(field idna-version)" # WHICH CHARACTER-DETECTION LIBRARY GOT BOUND is a fact about this image's # package list, not about requests: compat._resolve_char_detection() tries # "chardet" first and keeps it. This image carries both (charset_normalizer # since BRINTOS-2285), so chardet still wins; requests/__init__.py imports # charset_normalizer anyway, for its version check, so it is in sys.modules. check_eq $P/chardet-bound "chardet" "$(field chardet-bound)" check_eq $P/chardet-version "5.2.0" "$(field chardet-version)" check_eq $P/charset-normalizer-present "yes" "$(field charset-normalizer-present)" check_eq $P/charset-normalizer-version "3.3.2" "$(field charset-normalizer-version)" # simplejson IS PREFERRED OVER STDLIB json HERE, and that changes which # JSONDecodeError a malformed body raises. Pinned so it is visible the day # python3-simplejson leaves the image. check_eq $P/has-simplejson "True" "$(field has-simplejson)" check_eq $P/json-module "simplejson" "$(field json-module)" check_eq $P/jsondecodeerror-module "simplejson.errors" \ "$(field jsondecodeerror-module)" # packages.py performs an UNGUARDED __import__ of urllib3 and idna and must # preserve their identities -- that aliasing is the whole of the module. check_eq $P/packages-urllib3 "True" "$(field packages-urllib3-is-urllib3)" check_eq $P/packages-idna "True" "$(field packages-idna-is-idna)" # certs.where() is certifi.where() re-exported, and it must name a real file. check_eq $P/certs-where-is-certifi "True" "$(field certs-where-is-certifi)" check_eq $P/certs-where-isfile "yes" "$(field certs-where-isfile)" check_eq $P/certs-where "$SITE/certifi/cacert.pem" "$(field certs-where)" # THE pyopenssl SNI FALLBACK MUST STAY DEAD. HAS_SNI true is the condition that # keeps __init__.py:126-140 unreachable; python3-cryptography cannot be built on # this arm, so the branch firing would be an ImportError, not a fallback. check_eq $P/ssl-has-sni "True" "$(field ssl-has-sni)" check_eq $P/pyopenssl-not-injected "yes" "$(field pyopenssl-not-injected)" check_eq $P/cryptography-not-imported "yes" "$(field cryptography-not-imported)" # --- Session ---------------------------------------------------------------- # The object every consumer holds. A missing adapter mount means every request # raises InvalidSchema, which no import test would show. check_eq $P/session-max-redirects "30" "$(field session-max-redirects)" check_eq $P/session-stream "False" "$(field session-stream)" check_eq $P/session-verify "True" "$(field session-verify)" check_eq $P/session-trust-env "True" "$(field session-trust-env)" check_eq $P/session-adapters "http://,https://" "$(field session-adapters)" check_eq $P/session-adapter-class "HTTPAdapter" "$(field session-adapter-class)" check_eq $P/session-hooks "response" "$(field session-hooks)" check_eq $P/session-ua-prefix "python-requests" "$(field session-ua-prefix)" check_eq $P/session-ua "python-requests/2.32.4" "$(field session-ua)" check_eq $P/default-user-agent "python-requests/2.32.4" "$(field default-user-agent)" check_eq $P/default-headers-keys \ "Accept,Accept-Encoding,Connection,User-Agent" "$(field default-headers-keys)" # Accept-Encoding is assembled from urllib3's AVAILABLE decoders, so it is a # statement about the pair: brotli and zstd are absent on this image, gzip and # deflate come from zlib, which is python3-core. check_eq $P/session-accept-encoding "gzip, deflate" "$(field session-accept-encoding)" check_eq $P/adapter-pool-connections "10" "$(field adapter-pool-connections)" check_eq $P/adapter-pool-maxsize "10" "$(field adapter-pool-maxsize)" check_eq $P/adapter-max-retries "0" "$(field adapter-max-retries)" # --- PreparedRequest: everything before the socket -------------------------- check_eq $P/prep-method "GET" "$(field prep-method)" check_eq $P/prep-url "http://example.invalid/path?b=2&a=1+2" "$(field prep-url)" check_eq $P/prep-header "v" "$(field prep-header-xtest)" check_eq $P/prep-has-ua "yes" "$(field prep-has-ua)" check_eq $P/prep-body "None" "$(field prep-body)" check_eq $P/prep-data-body "k=v" "$(field prep-data-body)" check_eq $P/prep-data-ctype "application/x-www-form-urlencoded" \ "$(field prep-data-ctype)" check_eq $P/prep-data-clen "3" "$(field prep-data-clen)" check_eq $P/prep-json-body '{"k": "v"}' "$(field prep-json-body)" check_eq $P/prep-json-ctype "application/json" "$(field prep-json-ctype)" # IDNA IS EXERCISED FOR REAL. This is the one case that goes red if python3-idna # is dropped from RDEPENDS, and it cannot be reached by importing requests alone. check_eq $P/prep-idna-host "http://xn--nicode-2ya.invalid/" "$(field prep-idna-host)" # The documented refusals, as TYPED exceptions rather than tracebacks. check_eq $P/prep-missing-schema "EXC:MissingSchema" \ "$(field prep-missing-schema | cut -d: -f1-2)" check_eq $P/prep-invalid-url "EXC:InvalidURL" "$(field prep-invalid-url | cut -d: -f1-2)" # --- Response: everything after it, assembled from bytes by hand ------------ check_eq $P/resp-ok "True" "$(field resp-ok)" check_eq $P/resp-bool "True" "$(field resp-bool)" check_eq $P/resp-encoding "utf-8" "$(field resp-encoding)" check_eq $P/resp-text '{"k": "v", "n": 1}' "$(field resp-text)" check_eq $P/resp-json "v" "$(field resp-json-k)" check_eq $P/resp-raise-none "None" "$(field resp-raise-none)" check_eq $P/resp-is-redirect "False" "$(field resp-is-redirect)" check_eq $P/resp-links "{}" "$(field resp-links)" # apparent_encoding runs the character detector that compat bound above, so this # is a second, independent reading of which library got installed. check_eq $P/resp-apparent-encoding "ascii" "$(field resp-apparent-encoding)" check_eq $P/resp404-ok "False" "$(field resp404-ok)" check_eq $P/resp404-raise "EXC:HTTPError" "$(field resp404-raise | cut -d: -f1-2)" # A body that is not JSON raises the JSONDecodeError requests re-exports -- on # this image simplejson's, per has-simplejson above. check_eq $P/resp-badjson "EXC:JSONDecodeError" "$(field resp-badjson | cut -d: -f1-2)" check_eq $P/codes-ok "200" "$(field status-codes-ok)" check_eq $P/codes-not-found "404" "$(field status-codes-not-found)" check_eq $P/codes-teapot "418" "$(field status-codes-teapot)" # --- the utilities ---------------------------------------------------------- check_eq $P/requote-uri "http://example.invalid/a%20b?c=d%20e" "$(field requote-uri)" check_eq $P/parse-dict-header "[('a', 'b'), ('c', 'd')]" "$(field parse-dict-header)" check_eq $P/parse-list-header "['a', 'b, c', 'd']" "$(field parse-list-header)" check_eq $P/get-encoding-from-headers "ISO-8859-1" "$(field get-encoding-from-headers)" check_eq $P/unquote-unreserved "A%2F" "$(field unquote-unreserved)" check_eq $P/super-len "5" "$(field super-len)" check_eq $P/to-key-val-list "[('a', 1)]" "$(field to-key-val-list)" check_eq $P/guess-json-utf "utf-8" "$(field guess-json-utf)" check_eq $P/cid-get "text/plain" "$(field cid-get)" check_eq $P/cid-in "True" "$(field cid-in)" check_eq $P/cid-lower-items "[('content-type', 'text/plain')]" "$(field cid-lower-items)" # Cookies go through http.cookiejar, which is python3-netclient -- one of the # packages poky did not declare and the bbappend adds. check_eq $P/cookiejar-type "RequestsCookieJar" "$(field cookiejar-type)" check_eq $P/cookiejar-keys "a,b" "$(field cookiejar-keys)" check_eq $P/cookiejar-get "1" "$(field cookiejar-get)" check_eq $P/cookies-dict "[('a', '1'), ('b', '2')]" "$(field cookies-dict)" # Basic auth is base64 (python3-netclient); digest auth reaches hashlib # (python3-crypt). Both are bbappend additions, and this is where they are used. check_eq $P/basic-auth-header "Basic dTpw" "$(field basic-auth-header)" check_eq $P/basic-auth-str "Basic dTpw" "$(field basic-auth-str)" check_eq $P/digest-auth-type "HTTPDigestAuth" "$(field digest-auth-type)" # The exception hierarchy. A consumer catches RequestException; a port that # rebased the tree would break every caller's error handling silently. check_eq $P/exc-connectionerror-bases "RequestException" \ "$(field exc-connectionerror-bases)" check_eq $P/exc-httperror "True" "$(field exc-httperror-is-requestexception)" check_eq $P/exc-timeout "True" "$(field exc-timeout-is-requestexception)" check_eq $P/exc-ioerror "True" "$(field exc-requestexception-is-ioerror)" check_eq $P/exc-jsondecode "True" "$(field exc-jsondecodeerror-is-requestexception)" # --- CVE-2026-25645 as a behaviour ------------------------------------------ # Two extractions of the SAME member must land on DIFFERENT paths, and neither # on the member's own basename. The vulnerable code could produce neither: it # joined gettempdir() with the basename and reused whatever was already there, # which is the local file-preload vector. Nothing here is a claim about the # patch file; it is a claim about what the installed code does. check_eq $P/cve-extract-differs "True" "$(field cve-extract-differs)" check_eq $P/cve-extract-not-predictable "yes" "$(field cve-extract-not-predictable)" check_eq $P/cve-extract-content "payload" "$(field cve-extract-content)" check_eq $P/cve-extract-suffix ".txt" "$(field cve-extract-suffix)" # The early return the fix must not have disturbed: a path that is not inside an # archive comes back unchanged. check_eq $P/cve-extract-passthrough "/etc/hostname" "$(field cve-extract-passthrough)" # --- requests.help.info(), a second reading of the closure ------------------ # The diagnostic every bug report pastes. It imports ssl, idna and urllib3 and # probes charset_normalizer/chardet and pyopenssl, so it re-derives the closure # by a different route than compat.py did. check_eq $P/help-keys \ "chardet,charset_normalizer,cryptography,idna,implementation,platform,pyOpenSSL,requests,system_ssl,urllib3,using_charset_normalizer,using_pyopenssl" \ "$(field help-keys)" # THE PYOPENSSL-ABSENT SIGNATURE, and it is a dict rather than None: help.py # seeds pyopenssl_info with {'version': None, 'openssl_version': ''} and replaces # it only when OpenSSL imports, so None was never a value this field could take. # The expectation was derived, wrongly, from the image's package list while the # control host carried pyOpenSSL; it is now what the batch image measured. # `cryptography-absent' above asserts the same premise -- if this goes red, read # that case first, because the two fail together when pyOpenSSL appears. check_eq $P/help-pyopenssl "{'version': None, 'openssl_version': ''}" \ "$(field help-pyopenssl)" check_eq $P/help-urllib3 "2.2.2" "$(field help-urllib3)" check_eq $P/help-idna "3.7" "$(field help-idna)" check_eq $P/help-system-ssl-nonzero "yes" "$(field help-system-ssl-nonzero)" check_eq $P/help-using-charset-normalizer "False" \ "$(field help-using-charset-normalizer)" rm -rf "$W" 2>/dev/null