XLSX guide · OOXML inspection

XLSX Command-Line Inspection Guide: Macros, External Links and Sheet Names

When a workbook arrives from an upload, an automated export, or a vendor, you often need facts before opening it. These command-line techniques inspect the ZIP-based OOXML package or call the XLSX Inspector API. They are useful on CI runners and headless servers because they do not launch Excel or execute VBA.

PRACTICAL · COMMAND LINE READY

How do I check if an XLSX has macros without opening Excel?

A macro-enabled workbook normally has the .xlsm extension and contains a VBA project part named `xl/vbaProject.bin`. An ordinary .xlsx should not contain that part. Checking the package is a fast, low-risk first pass; it tells you whether VBA is present, not whether the VBA is safe.

unzip -l workbook.xlsm | grep -i 'xl/vbaProject.bin'

# API alternative
curl -F 'file=@workbook.xlsx' https://api.lifestep.io/inspect | jq '{has_macros, macro_parts}'

Why it works: OOXML files are ZIP containers. The central directory exposes member names without opening the workbook in a spreadsheet application. `vbaProject.bin` is the binary VBA project relationship used by macro-enabled Office files. Keep the extension and package result together: a renamed file can still reveal its contents, while a malformed archive may fail inspection. Never infer that “no macros” means “safe”; external links, formulas, embedded objects, and malformed XML are separate concerns.

How do I find external links in a workbook?

List the external-link XML parts and inspect their relationship targets. For a quick answer, the API reports external-link parts; for forensic detail, search the relationships and workbook XML for URLs or workbook paths.

unzip -l workbook.xlsx | grep -Ei 'externalLinks|externalLink'

unzip -p workbook.xlsx xl/_rels/workbook.xml.rels | grep -Ei 'Target=|external'

Why it works: Excel stores external workbook references in dedicated `xl/externalLinks/` parts and relationships from the workbook package. The target may be a URL, UNC path, or local path, so search output is evidence rather than a complete dependency graph. Relationship XML is escaped and namespaced, which is why a real XML parser is preferable for production tooling. The inspector’s bounded ZIP parsing gives you a safe summary before you decide whether to process the file further.

How do I list sheet names from the command line?

Use a small Python script with the standard library to read `xl/workbook.xml` and resolve the spreadsheet namespace. It prints names in workbook order, including hidden sheets.

python3 - <<'PY'
from zipfile import ZipFile
from xml.etree import ElementTree as ET
with ZipFile("workbook.xlsx") as z:
    root = ET.fromstring(z.read("xl/workbook.xml"))
    ns = {"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
    for sheet in root.findall("x:sheets/x:sheet", ns):
        print(sheet.attrib["name"], sheet.attrib.get("state", "visible"))
PY

Why it works: The workbook manifest is the authoritative place for sheet names and visibility state. Worksheet files can be numbered differently from the visible names, so reading `workbook.xml` avoids guessing from filenames. This script does not calculate formulas or load cell data. If the file came from an untrusted source, combine this metadata pass with archive-size limits and XML hardening before processing it in a larger pipeline.

A small production checklist

Make the command deterministic before putting it in automation. Pin the input hostname or filename, set a timeout, capture the exit status, and emit a concise error that a build log can explain. Treat an empty answer differently from a transport failure: an empty DNS record, a workbook with no matching package member, and an unavailable endpoint are different states. Test one known-good fixture and one deliberately bad fixture so a future dependency or API change cannot silently turn a failure into a pass. Prefer machine-readable JSON when an API provides it, but retain the original command for local diagnosis. If the result controls a user-facing decision, show the reason and a timestamp rather than only a green or red label. These habits keep a useful one-liner understandable when it becomes a scheduled check.