. "$BST_LIB" # BST-TIMEOUT: 1200 # SEVEN python3 starts: a bare `import', an `import-quiet' # # whose STDERR is the assertion, the probe (ONE start for # # 55 measurements), and four `binwalk' CLI invocations # # asserted by exit status. # # # # ~170s per start is the budget the neighbouring python3 # # suites use on this arm. The CLI starts cost the same as # # the library ones -- /usr/bin/binwalk is a three-line # # script and the work is the interpreter coming up plus # # the magic signature files being parsed, which is the one # # thing here that is not free: binwalk loads 23 magic files # # totalling ~340 KB at every start. # # # # NOTHING here forks except those four CLI runs, and none # # of them extracts, so there is no external-tool traffic to # # budget for. # # # # NOT MEASURED ON TARGET YET: this port's first on-target # # run is the batch verify. The measured elapsed replaces # # this note in the TEST PLAN, not in this comment, which # # would cost an image build to keep the shipped suite # # byte-identical. # # python3-binwalk 2.3.4 (meta-openembedded meta-python/recipes-devtools/python, # python3-binwalk_2.3.4.bb, `inherit setuptools3', git SRCREV cddfede) on the # wasm32-hwjs glibc image, with this project's one patch. # # ============================================================================ # WHAT THE PACKAGE IS, AND THEREFORE WHAT HAS TO BE TESTED. # ============================================================================ # # binwalk READS BYTES AND REPORTS OFFSETS. It matches a file against ~23 magic # signature files, prints what it recognised and where, and -- separately -- # plots the Shannon entropy of the file in 1024-byte windows. That scanning half # is entirely self-contained: no socket, no device, no terminal, no subprocess. # # Its OTHER half, extraction, shells out to third-party utilities. That half is # OUT OF SCOPE for this port and is asserted as out of scope below rather than # left untested. # # ============================================================================ # THE PORT IS ONE PATCH, AND IT IS NOT COSMETIC. # ============================================================================ # # binwalk/core/plugin.py line 4 is a module-scope `import imp'. `imp' was # removed in CPython 3.12 and this image runs 3.12.14, so the package as # meta-openembedded ships it CANNOT BE IMPORTED AT ALL here -- `import binwalk' # raises ModuleNotFoundError before anything else in this file could run. # # The patch replaces the three `imp.load_source' call sites with one importlib # helper, binwalk.core.common.load_source. `has-load-source', # `plugin-module-no-imp' and `imp-importable' are what say the patch is present # and that it is importlib, not a resurrected `imp', that is carrying it. # # ============================================================================ # THE PLUGIN LOADER IS TESTED THROUGH A RESULT, NOT THROUGH AN IMPORT. # ============================================================================ # # A patch that fixed the import and broke the loader would satisfy every case in # the paragraph above. So the suite reaches the loader through something only a # working loader can produce. # # binwalk ships 15 plugins that are loaded BY PATH through the patched helper, # and one of them decides a result: jffs2valid.py recomputes the header CRC32 of # every candidate JFFS2 node and clears result.valid when it does not match. # `scan-jffs2le-count' and `scan-jffs2-badcrc-count' are two fixtures that differ # in FOUR BYTES -- same magic, same nodetype, same totlen, one correct CRC and # one wrong one. The first is reported, the second is not. That difference exists # only if jffs2valid.py was really loaded and really ran. # # ============================================================================ # EVERY FIXTURE IS BUILT, NOT SHIPPED, AND CARRIES ONE STRUCTURE AT OFFSET 64. # ============================================================================ # # The probe writes each blob byte by byte: 64 zero bytes, the structure, 64 more # zero bytes. Committing binaries instead would make every assertion a comparison # against something a reader cannot check, and would leave nobody able to tell a # scanner bug from a bad fixture. Offset 64 rather than 0 is deliberate: a # scanner that only ever looks at the start of a file passes at offset 0. # # TWO OF THEM ARE MORE THAN MAGIC BYTES, AND BOTH TAUGHT SOMETHING: # # THE SQUASHFS SUPERBLOCK'S DECLARED SIZE MUST LAND INSIDE THE FILE. binwalk's # squashfs rule ends `{jump:%ld}' read from offset 40. A # superblock declaring 256 bytes in a 216-byte file is found, # marked valid=True and display=True, and then reported by # NOTHING in a default scan -- it appears only under # --invalid, which reads as a contradiction until the jump is # found. The fixture declares a size it actually has. # # THE JFFS2 HEADER CRC MUST BE RIGHT, and that is the plugin case above. # # ============================================================================ # THE TICKET EXPECTED TWO NON-ZERO EXITS. NEITHER IS WHAT THE PACKAGE DOES. # ============================================================================ # # Both were measured on an x86-64 control at this exact SRCREV, and the suite # asserts what was MEASURED. Stating it here because a reader who takes the # ticket's wording as the specification will read these two cases as bugs: # # A TRUNCATED SIGNATURE FILE EXITS 0. `binwalk -m ' prints # `Signature Exception: Invalid signature line: ...' and a # full Python traceback, and then exits SUCCESS. The scan it # was asked for never happened. `cli-badsig-rc' pins the 0 # because that is the shipped behaviour; `cli-badsig-says' # pins the diagnostic. A caller shell-scripting binwalk cannot # detect this failure from the exit status, which is worth # knowing and is not this port's to fix. # # A MISSING EXTRACTOR EXITS 0 TOO. See the extraction block below. # # The one path that DOES exit non-zero is a SCAN TARGET that is not there: # `cli-missing-rc' is 3. A missing SIGNATURE file is not that path -- it exits 0 # too (`cli-nomagic-rc'), which was measured only because the suite was # dry-run against the control before it shipped; read from a terminal the two # look alike, and the first reading of this pair had it wrong. # # ============================================================================ # EXTRACTION IS OUT OF SCOPE, AND THE TWO REFUSALS ARE ASSERTED. # ============================================================================ # # The extractor binwalk 2.3.4 wants for squashfs is `sasquatch' -- NOT # `unsquashfs', which is what the ticket named and what a reader would assume. # This image carries neither, nor jefferson, nor any of the others. # # Two measured behaviours, and which one a user meets depends on a flag: # # `--extract' ALONE, RUNNING AS ROOT, NEVER REACHES AN EXTRACTOR. The guest # runs as root, and binwalk refuses to execute third-party # utilities as root without being told to: it prints # `Extractor Exception: Binwalk extraction uses many third # party utilities, which may not be secure ... use # '--run-as=root'' and exits 3. So on this image the ordinary # extraction request fails at a security guard, before the # missing tools are ever looked for. # # `--extract --run-as=root' REACHES THEM AND EXITS 0. One WARNING per missing # extractor, the signature it found still printed, status 0. # # Nothing in this suite extracts anything: both cases are asserted on a fixture # that has no real filesystem in it, and neither can write outside $TMPDIR. P=python3-binwalk # --------------------------------------------------------------------------- bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/binwalk DI=$SITE/binwalk-2.3.3+cddfede.dist-info PKGLIST=/var/lib/dpkg/info/python3-binwalk.list PROBE=$BST_DATA/python3-binwalk-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-binwalk-suite.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # --- the module, as installed ---------------------------------------------- # setup.py declares packages=[MODULE_NAME] -- the TOP-LEVEL package only. The # five subdirectories ride along as package_data globs ("core/*", "modules/*", # "plugins/*", "magic/*", "config/*"), not as declared packages. They import # anyway, because core/ and modules/ carry __init__.py and plugins/ and magic/ # are namespace packages under Python 3 -- but it is why `plugins-no-init' # below asserts an ABSENCE that would be a packaging bug in any other layout. check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/not-a-flat-module test ! -e "$SITE/binwalk.py" check $P/init-file test -s "$PKGDIR/__init__.py" check $P/main-file test -s "$PKGDIR/__main__.py" # `test -f', not `test -s': core/__init__.py is EMPTY, which is the ordinary # shape of a package marker and not a truncated install. modules/__init__.py # next door is not empty -- it re-exports the module classes -- so the two are # asserted differently on purpose. check $P/core-subpackage test -f "$PKGDIR/core/__init__.py" check $P/core-magic test -s "$PKGDIR/core/magic.py" check $P/core-module test -s "$PKGDIR/core/module.py" check $P/core-plugin test -s "$PKGDIR/core/plugin.py" check $P/core-common test -s "$PKGDIR/core/common.py" check $P/core-version test -s "$PKGDIR/core/version.py" check $P/modules-subpackage test -s "$PKGDIR/modules/__init__.py" check $P/modules-signature test -s "$PKGDIR/modules/signature.py" check $P/modules-entropy test -s "$PKGDIR/modules/entropy.py" check $P/modules-extractor test -s "$PKGDIR/modules/extractor.py" check $P/plugins-dir test -d "$PKGDIR/plugins" check $P/plugin-jffs2valid test -s "$PKGDIR/plugins/jffs2valid.py" check $P/plugins-no-init test ! -e "$PKGDIR/plugins/__init__.py" check $P/config-extract test -s "$PKGDIR/config/extract.conf" # The magic signature files ARE the package: without them binwalk parses fine # and recognises nothing. They are plain text with no extension, which is # exactly the shape a packaging change is most likely to drop. check $P/magic-dir test -d "$PKGDIR/magic" check $P/magic-filesystems test -s "$PKGDIR/magic/filesystems" check $P/magic-compressed test -s "$PKGDIR/magic/compressed" check $P/magic-executables test -s "$PKGDIR/magic/executables" check $P/magic-images test -s "$PKGDIR/magic/images" check $P/magic-binwalk test -s "$PKGDIR/magic/binwalk" check $P/pycache-init test -s "$PKGDIR/__pycache__/__init__.cpython-312.pyc" # --- the console script ----------------------------------------------------- # setup.py ships this through `scripts=', not `entry_points', so it is a literal # three-line file and its shebang is whatever the build rewrote it to. Upstream # writes `#!/usr/bin/env python', which names an interpreter this image does not # have; Yocto's setuptools3 rewrites it to python3. That rewrite is the whole # reason the command works here, so it is asserted rather than assumed. check $P/bin-present test -x /usr/bin/binwalk check_out $P/bin-shebang "#!/usr/bin/env python3" head -n 1 /usr/bin/binwalk check_eq $P/bin-not-python2 "" \ "$(head -n 1 /usr/bin/binwalk | grep -E 'env python$' 2>/dev/null)" # --- the package, as dpkg has it ------------------------------------------- cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # This image's python3 has no dynamic loader at all (BRINTOS-1476), so a .so in # this list would be a file that could never be imported. binwalk is pure # Python and must carry none. check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # One script, and only one. check_eq $P/pkg-one-bin "/usr/bin/binwalk" \ "$(grep -E '^/usr/(s?bin)/' "$W/pkgf" 2>/dev/null)" # The upstream tree carries a `testing/' directory beside the package; setup.py # declares only `binwalk', so none of it must reach the rootfs. Asserted against # THIS package's file list rather than `test ! -d $SITE/testing': site-packages # is shared and the directory test would measure a neighbour. check_eq $P/no-testing-tree "" \ "$(grep -E "^$SITE/testing(/|\$)" "$W/pkgf" 2>/dev/null)" sed -n '/^Package: python3-binwalk$/,/^$/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" # The RECIPE is 2.3.4-r0. The PYTHON metadata is 2.3.3+cddfede. Both are # asserted, because they disagree and the disagreement is real -- see # dist-info-version below. check_out $P/pkg-version "Version: 2.3.4-r0" cat "$W/st" check_out $P/pkg-depends-compression "python3-compression" cat "$W/st" check_out $P/pkg-depends-logging "python3-logging" cat "$W/st" check_out $P/pkg-depends-netserver "python3-netserver" cat "$W/st" check_out $P/pkg-depends-setuptools "python3-setuptools" cat "$W/st" check_out $P/pkg-depends-stringold "python3-stringold" cat "$W/st" # --- the wheel metadata ---------------------------------------------------- check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-name "Name: binwalk" cat "$DI/METADATA" # THE VERSION IS NOT THE RECIPE'S VERSION, and this is not a packaging mistake. # setup.py hardcodes MODULE_VERSION = "2.3.3" and then appends `+' when it can run `git rev-parse' in the source tree -- which it can, # because Yocto's git fetcher unpacks a real clone. So the recipe FILENAME says # 2.3.4, the .deb says 2.3.4-r0, and everything Python can see says # 2.3.3+cddfede. binwalk.__version__ reads that metadata at import time through # importlib.metadata, so `version-attr' in the probe reports it too. # # It is asserted as a literal, including the hash, precisely because it is # derived from the SRCREV: a version bump that forgot to move the SRCREV, or a # fetcher change that stopped shipping .git, both land here. check_out $P/dist-info-version "Version: 2.3.3+cddfede" cat "$DI/METADATA" check $P/dist-info-record test -s "$DI/RECORD" # Purelib and a py3-none-any tag are the machine-readable form of "there is # nothing arch-specific in here", which is why this port is one patch and no # site answers. 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_eq $P/dist-info-no-arch-tag "" \ "$(grep '^Tag: ' "$DI/WHEEL" 2>/dev/null | grep -v '^Tag: py3-none-any$')" check_out $P/dist-info-top-level "binwalk" cat "$DI/top_level.txt" # --- import ---------------------------------------------------------------- # THE case this port exists for. Unpatched, this is a ModuleNotFoundError. check $P/import python3 -c 'import binwalk' # An import that WORKS but complains is a port that is not finished. On its own # start, so the noise, if any, is the whole of the evidence. check_eq $P/import-quiet "" \ "$(python3 -c 'import binwalk' 2>&1)" # --- the probe ------------------------------------------------------------- if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "$PROBE did not ship" rm -rf "$W" exit 0 fi bst_ok $P/probe-present bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } if [ -s "$OUT" ]; then bst_ok $P/probe-output else bst_notok $P/probe-output "the probe emitted nothing -- python3 did not start, or died before its first line; every CASE below is unscorable" fi # The probe swallows the one diagnostic it provokes on purpose, so anything on # its stderr is unexpected. check_eq $P/probe-quiet "" "$(cat "$OUT.err" 2>/dev/null)" check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "55" "$(pv emitted_count)" # --- the module, as the interpreter sees it -------------------------------- check_eq $P/module-answered "$PKGDIR/__init__.py" "$(pv module_file)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/version-attr "2.3.3+cddfede" "$(pv version_attr)" check_eq $P/has-scan "yes" "$(pv has_scan)" check_eq $P/has-execute "yes" "$(pv has_execute)" check_eq $P/all-names "ModuleException,execute,scan" "$(pv all_names)" # --- the patch, and the loader it exists for ------------------------------- check_eq $P/py-version "3.12" "$(pv py_version)" # The stdlib module the shipped package needed and 3.12 does not have. If this # ever reports `yes' the interpreter moved backwards, and the patch below is # then untested rather than unnecessary. check_eq $P/imp-importable "no" "$(pv imp_importable)" check_eq $P/has-load-source "yes" "$(pv has_load_source)" check_eq $P/plugin-module-no-imp "yes" "$(pv plugin_module_no_imp)" # The loader, run for real: 15 plugins found and loaded by path. check_eq $P/plugins-loaded "yes" "$(pv plugins_loaded)" check_eq $P/plugins-count "15" "$(pv plugins_count)" check_eq $P/plugin-jffs2valid-loaded "yes" "$(pv plugin_jffs2valid)" check_eq $P/plugin-gzipvalid-loaded "yes" "$(pv plugin_gzipvalid)" # --- the scan: one structure, one known offset, one asserted description ---- # Each description is a LITERAL, measured on an x86-64 control at this SRCREV. # They are literals and not substrings because the fields after the name are # where a byte-order or width mistake on a fresh arm would show: "64-bit LSB", # "block size = 900k", "blocksize: 131072 bytes" are all multi-byte reads. check_eq $P/scan-gzip-offset "64" "$(pv scan_gzip_offset)" check_eq $P/scan-gzip-count "1" "$(pv scan_gzip_count)" check_eq $P/scan-gzip-desc \ "gzip compressed data, maximum compression, last modified: 1970-01-01 00:00:00 (null date)" \ "$(pv scan_gzip_desc)" check_eq $P/scan-bzip2-offset "64" "$(pv scan_bzip2_offset)" check_eq $P/scan-bzip2-count "1" "$(pv scan_bzip2_count)" check_eq $P/scan-bzip2-desc "bzip2 compressed data, block size = 900k" \ "$(pv scan_bzip2_desc)" # The date in this one is `mkfs_time' rendered from the fixture's own 0x1000, # and it is UTC -- re-measured under TZ=UTC, America/Los_Angeles, Asia/Tokyo and # Europe/Moscow on the control, identical in all four. So it is safe as a # literal on a target whose clock and zone are not this suite's business. check_eq $P/scan-squashfs-offset "64" "$(pv scan_squashfs_offset)" check_eq $P/scan-squashfs-count "1" "$(pv scan_squashfs_count)" check_eq $P/scan-squashfs-desc \ "Squashfs filesystem, little endian, version 4.0, compression:gzip, size: 128 bytes, 4 inodes, blocksize: 131072 bytes, created: 1970-01-01 01:08:16" \ "$(pv scan_squashfs_desc)" # Both endiannesses of the same node type, which on a little-endian arm is the # pair most likely to catch a wrong assumption: the two fixtures differ only in # the byte order of three header fields. check_eq $P/scan-jffs2le-offset "64" "$(pv scan_jffs2le_offset)" check_eq $P/scan-jffs2le-count "1" "$(pv scan_jffs2le_count)" check_eq $P/scan-jffs2le-desc "JFFS2 filesystem, little endian" "$(pv scan_jffs2le_desc)" check_eq $P/scan-jffs2be-offset "64" "$(pv scan_jffs2be_offset)" check_eq $P/scan-jffs2be-count "1" "$(pv scan_jffs2be_count)" check_eq $P/scan-jffs2be-desc "JFFS2 filesystem, big endian" "$(pv scan_jffs2be_desc)" check_eq $P/scan-elf-offset "64" "$(pv scan_elf_offset)" check_eq $P/scan-elf-count "1" "$(pv scan_elf_count)" check_eq $P/scan-elf-desc "ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)" \ "$(pv scan_elf_desc)" check_eq $P/scan-jpeg-offset "64" "$(pv scan_jpeg_offset)" check_eq $P/scan-jpeg-count "1" "$(pv scan_jpeg_count)" check_eq $P/scan-jpeg-desc "JPEG image data, JFIF standard 1.01" "$(pv scan_jpeg_desc)" # A blob of zeros carries nothing. Without this case every assertion above is # consistent with a scanner that reports something for any input. check_eq $P/scan-zeros-nothing "0" "$(pv scan_zeros_count)" # THE PLUGIN CASE. Identical to scan-jffs2le except for the four CRC bytes, and # rejected -- by jffs2valid.py, which only a working patched loader ever ran. check_eq $P/scan-jffs2-badcrc-rejected "0" "$(pv scan_jffs2_badcrc_count)" # --- --offset / --length restrict the scan --------------------------------- # Two gzip members, at 64 and at 1024, in one 4096-byte file. The reported # offsets stay ABSOLUTE under --offset, which is the part a caller gets wrong: # the second member is reported at 1024, not at 0. check_eq $P/range-whole-count "2" "$(pv two_whole_count)" check_eq $P/range-whole-offsets "64,1024" "$(pv two_whole_offsets)" check_eq $P/range-offset-1024 "1024" "$(pv two_offset1024_offsets)" check_eq $P/range-length-512 "64" "$(pv two_length512_offsets)" check_eq $P/range-off64-len256 "64" "$(pv two_off64_len256_offsets)" # --- entropy over a buffer of known composition ---------------------------- # The high half is every byte value exactly 16 times, so each 1024-byte window # is perfectly uniform and the reading is EXACTLY 1.0 -- a value, not a # tolerance. The low half is zeros: exactly 0.0. The edge descriptions are what # say binwalk found the boundary at 4096 rather than merely reporting numbers. check_eq $P/entropy-rows "8" "$(pv entropy_rows)" check_eq $P/entropy-offsets "0,1024,2048,3072,4096,5120,6144,7168" "$(pv entropy_offsets)" check_eq $P/entropy-values \ "1.000000,1.000000,1.000000,1.000000,0.000000,0.000000,0.000000,0.000000" \ "$(pv entropy_values)" check_eq $P/entropy-high-at-0 "1.000000" "$(pv entropy_high_at_0)" check_eq $P/entropy-low-at-4096 "0.000000" "$(pv entropy_low_at_4096)" check_eq $P/entropy-rising-edge "Rising entropy edge (1.000000)" "$(pv entropy_first_desc)" check_eq $P/entropy-falling-edge "Falling entropy edge (0.000000)" "$(pv entropy_falling_desc)" # --- the two library-level error paths ------------------------------------- check_eq $P/badsig-raises "yes" "$(pv badsig_raised)" check_eq $P/badsig-type "ParserException" "$(pv badsig_type)" check_eq $P/missing-raises "yes" "$(pv missing_raised)" check_eq $P/missing-type "ModuleException" "$(pv missing_type)" # --- the command, as a user meets it --------------------------------------- # One real gzip member, written here rather than by the probe, so these cases # stand up even if the probe never ran. python3 -c 'import gzip,sys; sys.stdout.buffer.write(gzip.compress(b"cli fixture payload\n"*4, mtime=0))' > "$W/g.bin" 2>/dev/null check $P/cli-fixture test -s "$W/g.bin" # Each `binwalk' start re-parses the whole magic database, and that parse is # what a run costs on this guest -- so every group of cases below captures ONE # run and asserts against the capture, rather than re-running the same command # per case. This is the pattern cli-missing-says already used; it is now the # rule here, and it is what keeps the suite inside its wall-clock bound. _out=$(binwalk "$W/g.bin" 2>&1); _rc=$? check_eq $P/cli-scan-runs "0" "$_rc" case "$_out" in *"DECIMAL"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-scan-header "HIT" "$_hit" case "$_out" in *"gzip compressed data"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-scan-finds-gzip "HIT" "$_hit" check_eq $P/cli-scan-rc "0" "$_rc" # A path that is not there: the ONE error path that exits non-zero. _out=$(binwalk "$W/no-such-file.bin" 2>&1); _rc=$? check_eq $P/cli-missing-rc "3" "$_rc" # NOT check_out: that helper requires exit 0 AS WELL as the substring, and this # command exits 3 on purpose -- it would report a red whose detail showed the # wanted text present. The two cases here are deliberately separate: cli-missing-rc # owns the status, this one owns the diagnostic. case "$_out" in *"Cannot open file"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-missing-says "HIT" "$_hit" # A signature file the parser cannot read. Diagnostic yes, non-zero exit NO -- # see the header. This is the shipped behaviour, asserted as measured. printf '0\tstring\thsqs\tSquashfs filesystem,\n0\tstring\n' > "$W/bad.magic" _out=$(binwalk -m "$W/bad.magic" "$W/g.bin" 2>&1); _rc=$? case "$_out" in *"Invalid signature line"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-badsig-says "HIT" "$_hit" check_eq $P/cli-badsig-rc "0" "$_rc" # A signature file that is not there exits 0 as well, with a different # diagnostic. Stated beside the truncated case because the two together are the # whole of the -m failure surface, and NEITHER is detectable from the exit # status: whatever is wrong with the signature file, binwalk reports success # and scans nothing. _out=$(binwalk -m "$W/no-such.magic" "$W/g.bin" 2>&1); _rc=$? case "$_out" in *"No such file or directory"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-nomagic-says "HIT" "$_hit" check_eq $P/cli-nomagic-rc "0" "$_rc" # --- extraction, refused ---------------------------------------------------- # The guest is root, so the plain request meets the security guard and exits 3. # Nothing is extracted and nothing is written outside $W. _out=$(binwalk --extract --directory "$W" "$W/g.bin" 2>&1); _rc=$? check_eq $P/cli-extract-refused-rc "3" "$_rc" case "$_out" in *"Binwalk extraction uses many third party utilities"*) _hit=HIT ;; *) _hit=MISS ;; esac check_eq $P/cli-extract-refused-says "HIT" "$_hit" rm -rf "$W"