Skip to content

cobolwork 0.6.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 08:30
· 226 commits to main since this release

Summary

Findings witnessed by a run: abends from fuzzing, measured over 500 repositories.

  • ironwork's fuzzing found 1,372 abends that an input causes, in 105 of 500 held-out repositories, each reported with the input that caused it.
  • An input that keeps a loop running past a limit, or chooses the program a CALL loads, is a finding of its own.
  • A subprogram fuzzed at its interface reports what a caller passing those arguments would cause, gated by its own policy key.
  • A scan reports credentials written into COBOL source without gitleaks installed, and the credential rules read the quoted, phrase and hexadecimal forms IBM documents.
  • HLASM statements are parsed by kind and placed as the assembler places them, with no location differing from z390's.
  • A full scan reads and parses each program once for all its rule sets, and the flow set reads every program of a 3,263-program repository within memory.

Limit: reading a subprogram fuzzed at its interface needs ironwork after 0.4.1, which writes that format.

What 0.6.0 adds over 0.5.0

Install: npm install -g @portll/cobolwork or pip install cobolwork; either gives the command cobolwork. The PyPI package runs the same files with the Node.js on your PATH, which must be Node 22 or later. The npm package is attached here as cobolwork-0.6.0.tgz to pin, and as cobolwork.tgz.

Abends as findings, measured

  • Over the 500 held-out repositories, ironwork's fuzzing, as released in ironwork 0.4.0, ran 1,637 of 21,971 candidate programs, 163,700 runs in all. The abend set, with cobolwork at 2bd77a4, reports 1,372 findings in 105 repositories: S0C7 1,333, S0CB 17, U0999 16, and 6 file-status abends. 744 of them are in one repository of generated copies; 628 are in the other 104. More than half the programs fuzzing could not run take USING parameters: they are subprograms, which the interface fuzzing below reaches.
  • input-causes-hang (medium, CWE-834) reports a timed-out input that, run again under a statement limit, ends in S322. input-selects-program (high, CWE-470) reports an S806 whose run's journal shows the marker reaching the CALL's program load; a manifest alone cannot make one.
  • A data or protection exception inside a CICS task, which ironwork reports as ASRA as CICS does, is reported under the S0C7 or S0C4 rule, chosen from the program check ironwork names.
  • Each abend says whether it recurred when the program was compiled with OPTIMIZE(2). An optimized build may compare an unsigned zoned item with zero by its bytes where the default build raises a data exception. The finding keeps its rule and severity.
  • An abend from ironwork fuzz --job is placed in the COBOL step that gave it, with the data set, in-stream SYSIN and step PARM that caused it. A fuzz manifest in a format the set does not read leaves the set incomplete instead of being read.
  • A subprogram fuzzed at its interface (ironwork fuzz --interface, format ironwork-fuzz-interface/v1) gives findings with abend.inputFrom interface and the CALLs its arguments were drawn from. Its detail says no caller run shows a caller passes those bytes. The policy key interface takes warn, the default, which never blocks a build, or tier, which gates them like any other finding. Earlier versions refuse the key.

Credentials

  • The secrets set reports credential-in-source (high, CWE-798) without gitleaks: a literal VALUE on an item named for a credential, and a literal password in EXEC SQL CONNECT or EXEC CICS SIGNON, VERIFY or CHANGE. It reads the shapes from rules/gitleaks-mainframe.toml, so one list serves both. It names the item or statement, never the value. Over the 500 held-out repositories it reports the 23 findings in 9 repositories that gitleaks does. The build gate warns on it until its firing rate is recorded, unless the policy names it block.
  • The credential rules read the forms IBM documents: a JCL password or password phrase in apostrophes, continued past column 71 if need be; a quoted new value in PASSWORD=(old,new); RACF PHRASE('...') and the PASSWORD and PHRASE commands; a TSO LOGON new password; EXEC CICS NEWPASSWORD; a hexadecimal or continued COBOL VALUE; and a COBOL debugging line. New rules: tso-logon-new-password, racf-command-new-password and cics-new-password. A comment line with / in column 7, an all-space or all-zero hexadecimal fill, a hexadecimal value under four bytes and a symbolic parameter in apostrophes are not credentials. The 500 held-out repositories hold none of the new forms, so the counts are unchanged.

HLASM

  • Each statement is parsed by its kind. Machine instructions are matched against rules/hlasm-instructions.json: 2,388 mnemonics with format, op code, operand syntax and length from the z/Architecture Principles of Operation, with the page. Assembler instructions, system macros (MODESET, RACROUTE, LINK, XCTL, LOAD, ATTACH, OPEN, CLOSE, DCB, GET, PUT, WTO, ESTAE and others), Language Environment and Toolkit macros, macro prototypes and model statements each have a parser. EXEC CICS, EXEC SQL and EXEC DLI in assembler are read by the same code that reads them in COBOL. Outside conditional assembly, 99.5% of statements parse on the development corpus and 99.7% on the held-out one.
  • The locator places location counters, symbols, lengths, literal pools in the Language Reference's five segments, COPY members and the external names a module needs, as the assembler would. Graded against z390 on values no macro expansion changes, it places 935 of 936 symbol locations and 2,511 of 2,546 statement locations on the development corpus, and 546 of 557 and 1,631 of 1,675 on the held-out one, none differing. Values z390 gives otherwise than the Language Reference, an EQU's length and anything after a literal pool z390 pads, are not graded.
  • hlasm-runtime-module-name (medium, CWE-470) reports LINK, XCTL, LOAD and ATTACH given EPLOC= or DE=, which name the module by the address of its name. A finding on EX or EXRL names the instruction it runs when the target is in the same file. A refused statement or a COPY member the tree lacks marks the set's coverage incomplete and is named.

Scan time and memory

Every report is identical to the previous commit's, peak memory aside, on the repositories each change was checked on.

  • A full scan reads and parses each program once, where the inventory and eight rule sets each parsed it: one tenth of Unieuro scans in 168 s against 369 s, and ACAS in 59 s against 92 s.
  • The flow set keeps each distinct fact set and check condition once, holds its edges in columns, and numbers call contexts. It reads all 3,263 programs of Unieuro in 8.6 GB, where it stopped at the memory reserve after 2,626, and ACAS's flow set takes 62 to 68 s against 83 to 87 s.
  • A program of 256 KiB or more that another file repeats byte for byte has its control flow analysed once for every copy: cnafbadboy's full scan takes 589 s against 928 s. COBOLWORK_VERIFY_REUSE=1 analyses every copy afresh as well and stops if the two differ.

Evidence, gate and flow

  • build fails the check where an equivalence statement may be required and no --allowed-signers is given, and refuses a signers file inside the repository. evidence verify takes --anchor-pin, --tsr (imprint, nonce and signature) and --cosign-bundle. One contradicting witness makes the verification report the evidence as not sealed. Two writers that find the same stale ledger lock no longer fork the ledger.
  • evidence verify reads ironwork's statement records and the input-taint field of sink records. A close record's RETURN-CODE may be negative. test/fixtures/evidence/kinds.tsv and sinks.tsv publish the record kinds and the sink kinds, which ironwork tests its writer against.
  • capabilities names the ironwork releases cobolwork reads: each format with the first ironwork release that writes it, 0.4.1 as the minimum, and the run endings by exit status.
  • The build's options check fails a check the pinned GnuCOBOL cannot generate (-fec needs 3.1, EC-PROGRAM-ARG-MISMATCH needs 3.2), naming the file that pins the older release.
  • A scan given a relative root resolves it. Before, it found no files and reported its coverage complete. An empty or unreadable root reports incomplete coverage.
  • A job's in-stream data reaches the item a program READs it INTO. ironwork check is read by its exit code first, and the exit statuses ironwork keeps for itself (241 to 246, 255) give the labeller an unknown label that names the reason.
  • cobolwork.baseline.json carries a version. A file without one is read as version 0 and migrated, and a newer one is refused, naming both numbers.
  • The labeller feeds a job's in-stream data and stages ARITH(EXTEND) for a program that needs it. It labels GnuCOBOL command-line and environment input on a copy with those ACCEPTs rewritten, and an interface finding by whether a caller's run confirms it; bench/precision.mjs counts each of these as a stratum of its own, never with execution labels. An unknown label whose operation ran without the marker records what input taint found there. No label is refuted on it; docs/spec/reach.md 9.8 says why.
  • SECURITY.md lists, per command, the files it reads and writes, the processes it starts and whether it reaches the network.

The package's lib/revision.json names the commit it was built from.