@portll/cobolwork 0.0.1 → 0.2.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +661 -0
  2. package/LICENSING.md +93 -0
  3. package/NOTICE +9 -0
  4. package/README.md +325 -3
  5. package/THIRD-PARTY-NOTICES.md +118 -0
  6. package/bin/cobolwork.mjs +354 -0
  7. package/lib/advisories.mjs +133 -0
  8. package/lib/baseline.mjs +154 -0
  9. package/lib/bms.mjs +453 -0
  10. package/lib/build.mjs +402 -0
  11. package/lib/capabilities.mjs +79 -0
  12. package/lib/cics-commands.mjs +281 -0
  13. package/lib/compliance.mjs +81 -0
  14. package/lib/consequence.mjs +139 -0
  15. package/lib/control.mjs +1515 -0
  16. package/lib/csd.mjs +77 -0
  17. package/lib/dataflow.mjs +1506 -0
  18. package/lib/diff.mjs +344 -0
  19. package/lib/explain.mjs +145 -0
  20. package/lib/gate.mjs +383 -0
  21. package/lib/index.mjs +6 -0
  22. package/lib/inventory.mjs +79 -0
  23. package/lib/jcl.mjs +478 -0
  24. package/lib/kernel/findings.mjs +94 -0
  25. package/lib/kernel/identity.mjs +216 -0
  26. package/lib/kernel/memory.mjs +217 -0
  27. package/lib/kernel/printable.mjs +6 -0
  28. package/lib/kernel/registry.mjs +79 -0
  29. package/lib/kernel/ruleset.mjs +72 -0
  30. package/lib/kernel/source-tree.mjs +159 -0
  31. package/lib/kev.mjs +27 -0
  32. package/lib/options.mjs +512 -0
  33. package/lib/packs.mjs +148 -0
  34. package/lib/parser.mjs +2055 -0
  35. package/lib/policy.mjs +163 -0
  36. package/lib/precompile-cics.mjs +169 -0
  37. package/lib/precompile.mjs +544 -0
  38. package/lib/reach.mjs +122 -0
  39. package/lib/revision.json +1 -0
  40. package/lib/revision.mjs +89 -0
  41. package/lib/sarif.mjs +222 -0
  42. package/lib/scan.mjs +272 -0
  43. package/lib/sets/build.mjs +234 -0
  44. package/lib/sets/cics.mjs +306 -0
  45. package/lib/sets/compile.mjs +187 -0
  46. package/lib/sets/copybook.mjs +174 -0
  47. package/lib/sets/flow.mjs +487 -0
  48. package/lib/sets/hidden.mjs +216 -0
  49. package/lib/sets/jcl.mjs +440 -0
  50. package/lib/sets/log.mjs +406 -0
  51. package/lib/sets/opaque.mjs +102 -0
  52. package/lib/sets/priv.mjs +322 -0
  53. package/lib/sets/recon.mjs +267 -0
  54. package/lib/sets/vendor.mjs +117 -0
  55. package/lib/sets/web.mjs +327 -0
  56. package/lib/site.mjs +164 -0
  57. package/lib/sources.mjs +156 -0
  58. package/lib/tui/app.mjs +325 -0
  59. package/lib/tui/keys.mjs +39 -0
  60. package/lib/tui/model.mjs +96 -0
  61. package/lib/tui/run.mjs +38 -0
  62. package/lib/tui/screen.mjs +59 -0
  63. package/lib/tui/terminal.mjs +46 -0
  64. package/lib/utilities.mjs +296 -0
  65. package/lib/version.mjs +15 -0
  66. package/lib/words.mjs +318 -0
  67. package/package.json +45 -6
  68. package/rules/advisories.json +264 -0
  69. package/rules/compliance-dora.json +2151 -0
  70. package/rules/compliance-ffiec.json +2134 -0
  71. package/rules/compliance-nist80053.json +2134 -0
  72. package/rules/gitleaks-mainframe.toml +57 -0
  73. package/rules/kev-ids.json +1729 -0
  74. package/rules/packs/broadcom.json +124 -0
  75. package/rules/packs/connectdirect.json +116 -0
  76. package/rules/packs/controlm.json +114 -0
  77. package/rules/system-layouts.json +28 -0
  78. package/schema/cobolwork-coverage.schema.json +65 -0
  79. package/schema/cobolwork.policy.schema.json +54 -0
package/LICENSING.md ADDED
@@ -0,0 +1,93 @@
1
+ # Licensing
2
+
3
+ cobolwork is published under **AGPL-3.0-or-later**. That is the licence in [`LICENSE`](LICENSE), the
4
+ one in `package.json`, and the one every published artifact carries. If AGPL works for you, take it:
5
+ nothing here asks you to talk to us first.
6
+
7
+ Other terms are available on application, for organisations whose policy will not approve AGPL. This
8
+ page says what those are, what they cost you in guarantees, and what a grant does and does not reach.
9
+ It is written so that a reviewer can read it once and decide.
10
+
11
+ ## If you are reading this because AGPL was refused
12
+
13
+ Two things are worth knowing before you ask for anything.
14
+
15
+ **Running cobolwork over your own source almost certainly triggers nothing.** cobolwork is a
16
+ command-line scanner. It does not serve a network interface, so the AGPL's §13 — the clause that
17
+ separates it from GPL — has nothing to act on. Unmodified internal use owes you no obligation beyond
18
+ keeping the notices. If your reviewer's objection is "AGPL means we must publish our source", that is
19
+ not what this licence does here, and the cheapest path is to show them that sentence.
20
+
21
+ **What actually needs other terms** is modifying cobolwork and not publishing the modifications;
22
+ embedding it in something you ship; offering it to third parties as part of a product or a service;
23
+ or an internal policy that refuses copyleft on sight regardless of what the clauses say. That last one
24
+ is a real constraint and we do not argue with it.
25
+
26
+ ## What is on offer
27
+
28
+ | | For | Terms |
29
+ |---|---|---|
30
+ | **Public** | Anyone | AGPL-3.0-or-later. Free. No application, no conversation. |
31
+ | **Evaluation** | Assessing it before committing | [PolyForm Free Trial 1.0.0](https://polyformproject.org/licenses/free-trial/1.0.0/), unmodified |
32
+ | **Internal use** | An organisation running it over its own code, whose policy refuses AGPL | [PolyForm Internal Use 1.0.0](https://polyformproject.org/licenses/internal-use/1.0.0/), unmodified |
33
+ | **Everything else** | Redistribution, embedding, OEM, delivery inside a consultancy engagement | Negotiated commercial licence |
34
+
35
+ PolyForm Internal Use does not permit you to offer the software to third parties. If you are a
36
+ consultancy or a modernisation partner wanting to run cobolwork inside client engagements, that is
37
+ the negotiated tier, not the internal-use one.
38
+
39
+ We do not offer PolyForm Noncommercial or PolyForm Small Business. Anyone eligible for either can
40
+ already take AGPL for nothing, so the tier would exist only to look generous.
41
+
42
+ **A PolyForm grant is not the same product as a commercial contract.** PolyForm licences are
43
+ as-is: no warranty, no indemnity, no liability cap, no confidentiality, no support commitment. If your
44
+ vendor-onboarding process requires those — most banks' does — you want the negotiated tier. Ask for
45
+ it directly.
46
+
47
+ **None of the alternative tiers is open source.** PolyForm licences are source-available and are not
48
+ OSI-approved. The AGPL distribution is open source; a PolyForm grant is not, and we will not describe
49
+ it as one.
50
+
51
+ ## The word lists
52
+
53
+ [`lib/words.mjs`](lib/words.mjs) holds the COBOL reserved words, registers, system names and intrinsic
54
+ functions the parser recognises. They are derived from online sources - ISO/IEC 1989 drafts published
55
+ by JTC 1/SC 22/WG 4 and by INCITS, and the language references of IBM, Micro Focus, ACUCOBOL-GT,
56
+ RM/COBOL, Fujitsu, BS2000, Bull GCOS and Veryant - and validity-checked.
57
+ [`provenance/words.json`](provenance/words.json) records, for every word, which document attests it,
58
+ with that document's URL, retrieval date and SHA-256. `lib/words.mjs` is generated from that record and
59
+ from nothing else, and a test in the suite asserts the two agree and that every word is a well-formed
60
+ COBOL word, so no word can enter the parser's vocabulary without a source behind it.
61
+
62
+ ### What a grant conveys
63
+
64
+ A grant on terms other than AGPL conveys a release artifact or a tagged source tree.
65
+
66
+ ### What still needs a practitioner
67
+
68
+ One question should be answered in writing before money changes hands. Whether a compiler's word list
69
+ attracts copyright at all is open: IceTV v Nine Network [2009] HCA 14 and Telstra v Phone Directories
70
+ [2010] FCAFC 149 refuse sweat-of-the-brow compilation copyright, and Directive 2009/24/EC art 1(2) with
71
+ SAS Institute v World Programming put a language's elements outside protection. A paid grant reads as a
72
+ warranty of title over the whole work, and a warranty deserves an opinion rather than a confident
73
+ paragraph.
74
+
75
+ ## Contributions
76
+
77
+ [`CLA.md`](CLA.md) governs contributions, and CLA assistant asks each contributor to sign it on their
78
+ first pull request. It permits this dual arrangement, which is the point of it: without a CLA,
79
+ a single outside contribution would make every alternative tier impossible from that day, because
80
+ each contributor would hold rights nobody could relicense.
81
+
82
+ ## Asking
83
+
84
+ Write to <john@portll.net> with: your organisation, which tier you think you need, what you intend to
85
+ do with cobolwork, and — if your policy refuses AGPL — the clause your reviewer objects to. That last
86
+ one saves a round trip and occasionally saves the whole conversation, because the objection is often
87
+ to something the AGPL does not require here.
88
+
89
+ ---
90
+
91
+ *This page describes an intention to license and is not itself an offer, a contract or legal advice.
92
+ Each grant is a separate agreement, made against a release artifact or a tagged tree rather than
93
+ against this repository's history, for the reason given above.*
package/NOTICE ADDED
@@ -0,0 +1,9 @@
1
+ cobolwork — COBOL, JCL and CICS security analysis
2
+ Copyright (C) 2026 Portll <john@portll.net>
3
+
4
+ This program is free software: you can redistribute it and/or modify it under the terms of the
5
+ GNU Affero General Public License as published by the Free Software Foundation, either version 3
6
+ of the License, or (at your option) any later version. See LICENSE.
7
+
8
+ A commercial licence is available for those whose policy or product cannot accept the AGPL.
9
+ Contact john@portll.net.
package/README.md CHANGED
@@ -1,5 +1,327 @@
1
- # @portll/cobolwork
1
+ # cobolwork
2
2
 
3
- This name is reserved by [Portll](https://github.com/Portll). There is no published release yet.
3
+ [![CI](https://github.com/Portll/cobolwork/actions/workflows/ci.yml/badge.svg)](https://github.com/Portll/cobolwork/actions/workflows/ci.yml)
4
+ [![Node](https://img.shields.io/badge/node-%E2%89%A518-informational)](https://nodejs.org)
5
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-informational)](package.json)
6
+ [![Licence](https://img.shields.io/badge/licence-AGPL--3.0--or--later-informational)](LICENSE)
4
7
 
5
- Watch [github.com/Portll/cobolwork](https://github.com/Portll/cobolwork) for the first one.
8
+ Cobolwork offers security analysis for COBOL, JCL and CICS, with no runtime dependencies. It reads
9
+ the source the way a compiler does and follows untrusted data across program boundaries.
10
+
11
+ It runs on its own. It can also run as a lane inside commitwork, Portll's CI and security runner.
12
+
13
+ ## Why it exists
14
+
15
+ Nothing open source reads COBOL for security. Measured 2026-09-16: Semgrep, CodeQL, SonarQube
16
+ Community Edition and PMD ship no COBOL rules; gitleaks and TruffleHog have no rule for a RACF
17
+ password, a JCL `PASSWORD=` or a TSO logon; NIST's SARD holds zero COBOL cases. The one open-source
18
+ COBOL scanner, VisualCodeGrepper, matches regular expressions within a single file.
19
+
20
+ Meanwhile a Semgrep run over a COBOL-only repository loads three thousand rules, reads none of its
21
+ COBOL, exits zero and reports a clean result.
22
+
23
+ ## Install
24
+
25
+ npm install -g @portll/cobolwork
26
+ pip install cobolwork
27
+
28
+ Either installs the command `cobolwork`; the PyPI package runs the same files with the Node.js on
29
+ your PATH. The same package is attached to each
30
+ [release](https://github.com/Portll/cobolwork/releases) as `cobolwork-<version>.tgz`, which is the
31
+ one to pin, and as `cobolwork.tgz`, which
32
+ `npm install -g https://github.com/Portll/cobolwork/releases/latest/download/cobolwork.tgz`
33
+ follows. To run from a checkout instead:
34
+
35
+ git clone https://github.com/Portll/cobolwork && cd cobolwork && npm link
36
+
37
+ Node 18 or later. No dependencies, runtime or development. `npm link` puts `cobolwork` on your PATH;
38
+ adding `bin/` to PATH does the same thing. GnuCOBOL is needed only to regrade the parser or validate
39
+ benchmark cases.
40
+
41
+ ## What it does
42
+
43
+ | Command | Information |
44
+ |---|---|
45
+ | `cobolwork scan <path>` | every rule set below, as JSON or SARIF |
46
+ | `cobolwork inventory <path>` | inventory (and what couldn't be read) |
47
+ | `cobolwork flow <path>` | where untrusted data reaches a sensitive operation, with the path it took |
48
+ | `cobolwork diff <repo> --base <ref>` | what a change reaches: layouts it moves in programs nobody edited, new call targets, findings it adds or removes |
49
+ | `cobolwork build <repo> [--base <ref>] [-- <compiler> …]` | the build gate: every finding ranked LOW to KNOWN-EXPLOITABLE, the build stopped on the ones the policy blocks and on compiler options that let a bad index corrupt storage, and the compiler run only on a pass |
50
+ | `cobolwork parse <file>` | one file's structure, for debugging |
51
+
52
+ ## What it finds
53
+
54
+ | Area | What is reported |
55
+ |---|---|
56
+ | Data flow | untrusted input - the command line, a job's `PARM` or in-stream data, a CICS terminal or web request - reaching an OS command, dynamic SQL, a dynamic `CALL`, `LINK` or `XCTL`, a file name, the internal reader, a subscript or length, decimal arithmetic, a record key or a log, across programs |
57
+ | Checks | a check counts only where it runs first, on every route; one that leaves the value safe for the sink clears the route |
58
+ | Screen fields | a field the BMS map protects, read back and used to choose a record: the 3270 hidden form field |
59
+ | Exfiltration | database rows and file records leaving through web calls, sockets, MQ, extrapartition queues or service calls |
60
+ | CICS and CALL | a communication area read without `EIBCALEN`, a transfer to a variable program, a length longer than the area or the callee, a route around a sign-on |
61
+ | Privilege and logs | diagnostic transactions installed, command security off where it is used, credentials or personal data written to a log, input forging a log line, system error codes sent to a web client |
62
+ | JCL | credentials and security commands in in-stream data, destructive statements, `DLM=` tricks, FTP in cleartext or sending production data, production data touched by a test job |
63
+ | The source | names nothing declares (code that cannot compile), shadowed copybooks, payloads hidden in columns 73-80 or aimed at AI readers |
64
+ | The estate | production names outside production jobs, routable addresses, compiler and runtime versions with published advisories |
65
+
66
+ [docs/rule-sets.md](docs/rule-sets.md) describes each in full, with what it deliberately leaves out.
67
+ Vendor packs for CA ACF2 and Top Secret, Control-M and Connect:Direct load only for estates that
68
+ name them, and `rules/gitleaks-mainframe.toml` gives gitleaks the mainframe credential shapes it
69
+ lacks.
70
+
71
+ ## What a finding looks like
72
+
73
+ The flaw is in neither file on its own: one program reads the command line and hands it on, the
74
+ other runs what it was handed. Both commands below run against this repository, so every number
75
+ here can be checked.
76
+
77
+ $ cobolwork flow test/fixtures/dataflow
78
+
79
+ ```json
80
+ {
81
+ "rule": "argv-or-env-to-os-command",
82
+ "sev": "crit",
83
+ "cwe": "CWE-78",
84
+ "path": "pos/P2.cbl",
85
+ "line": 10,
86
+ "program": "P2",
87
+ "crossProgram": true,
88
+ "hops": 4,
89
+ "detail": "Command-line or environment input reaches an operating-system command routine: ACCEPT ... FROM COMMAND-LINE at pos/P1.cbl:8 reaches CALL 'SYSTEM' USING WS-LOCAL",
90
+ "trace": [
91
+ { "program": "P1", "item": "WS-IN", "file": "pos/P1.cbl", "via": "source" },
92
+ { "program": "P1", "item": "WS-CMD", "file": "pos/P1.cbl", "via": "MOVE at pos/P1.cbl:9" },
93
+ { "program": "P2", "item": "LK-CMD", "file": "pos/P2.cbl", "via": "CALL 'P2' argument 1 at pos/P1.cbl:10" },
94
+ { "program": "P2", "item": "WS-LOCAL", "file": "pos/P2.cbl", "via": "MOVE at pos/P2.cbl:9" }
95
+ ],
96
+ "related": [{ "path": "pos/P1.cbl", "line": 8, "detail": "ACCEPT ... FROM COMMAND-LINE" }],
97
+ "sources": 1,
98
+ "evidence": "path",
99
+ "fingerprint": "45e55b502c397a499cc29212c0c28329"
100
+ }
101
+ ```
102
+
103
+ `crossProgram` marks a path that left the file it started in. Findings carry no source text, so a
104
+ report can be stored and passed on without carrying the code with it.
105
+
106
+ `fingerprint` is what the finding is, rather than where it is printed today: the rule, the program
107
+ and the paragraph or section it sits in (the job, step and DD for JCL), and the flagged statement's
108
+ own text. No line number goes into it, so code added above a finding does not change it. `diff`
109
+ compares findings by it, and SARIF carries it as `partialFingerprints["cobolwork/v1"]`. Two findings
110
+ that only their position tells apart share one, and `summary.identity.shared` counts them.
111
+
112
+ ### Findings and Claim Severity
113
+
114
+ Severity says how urgent a finding is. `evidence` says what the tool actually established, which
115
+ decides who acts on it. Every rule declares one of eight finding types:
116
+
117
+ | `evidence` | What the finding claims | Who acts |
118
+ |---|---|---|
119
+ | `path` | untrusted input was traced to a sensitive operation, and `trace` is the route | the program's owner |
120
+ | `construct` | the construct is a defect wherever it sits; no input has to reach it | the owner, or whoever can rotate a credential |
121
+ | `tampering` | the source is arranged so a reader or resolver sees something other than what runs | a reviewer, before merge |
122
+ | `advisory` | a pinned compiler or runtime matches a published advisory | whoever owns the build |
123
+ | `exposure` | information about the estate is written into source | the owner |
124
+ | `change` | a change moves an interface or adds a call target (`diff` only) | the reviewer of that change |
125
+ | `coverage` | the analysis stopped following here | nobody's code; read more, or accept the limit |
126
+ | `context` | describes the estate (an entry point, a product in use) | nobody; it asserts no defect |
127
+
128
+ `coverage` and `context` are exactly the `info` rules (not defects). A consumer that counts these
129
+ findings should leave them out of its counts.
130
+
131
+ None of the kinds says *exploitable*, on purpose. Whether a route can be used also depends on who may
132
+ start the transaction or job that reaches it, and on what the running system enforces, and no
133
+ repository holds those facts. A finding names the entry points that reach it (`startedBy`). `path`
134
+ is the strongest claim the tool makes: a route found by reading the code, not by running it. Its
135
+ precision has been measured on benchmark cases this project wrote, not yet on an independently
136
+ labelled corpus.
137
+
138
+ ### What each finding lets someone do, and the fix
139
+
140
+ A defect finding carries two more facts, the same for every finding of its rule: what someone can do
141
+ once the route or construct is present, and the standard fix. They are what makes a `path` or
142
+ `construct` finding read as a hole to act on rather than a location. A report carries them once per
143
+ rule, keyed by rule id beside `ruleText` and `ruleCwe`, so a finding does not repeat them. For the
144
+ finding above:
145
+
146
+ ```json
147
+ "ruleImpact": { "argv-or-env-to-os-command": "Whoever sets the program's command line or environment runs an arbitrary operating-system command with the program's authority" },
148
+ "ruleRemedy": { "argv-or-env-to-os-command": "Build the command only from fixed literals; never place input in the argument of CALL 'SYSTEM' or BPXWDYN. If it must vary, choose from an allow-list of known commands" }
149
+ ```
150
+
151
+ The `who` is the finding's own — the entry points it carries in `startedBy` — and the impact
152
+ completes it. SARIF puts the impact in each rule's `fullDescription` and the fix in its `help`, which
153
+ is where GitHub code scanning shows a recommendation. The `info` kinds carry neither, because a fix
154
+ would assert a defect they do not claim; a site-gated rule carries them once the estate's fact turns
155
+ it into a defect, and not before.
156
+
157
+ ### What it could not read
158
+
159
+ A scan says what each rule set read, not only what it found:
160
+
161
+ $ cobolwork scan bench/cases --quiet
162
+
163
+ ```json
164
+ "summary": {
165
+ "findings": 127,
166
+ "bySeverity": { "high": 28, "crit": 16, "med": 16, "low": 9, "info": 58 },
167
+ "byEvidence": { "tampering": 6, "path": 31, "advisory": 1, "construct": 31, "coverage": 6, "context": 52 },
168
+ "bySet": {
169
+ "inventory": { "filesScanned": 83, "filesUnreadable": 0 },
170
+ "flow": { "filesScanned": 83, "filesUnreadable": 0 },
171
+ "cics": { "filesScanned": 42, "filesUnreadable": 0 },
172
+ "hidden": { "filesScanned": 116, "filesUnreadable": 0 },
173
+ "copybook": { "filesScanned": 88, "filesUnreadable": 0 },
174
+ "jcl": { "filesScanned": 28, "filesUnreadable": 0 },
175
+ "build": { "filesScanned": 2, "filesUnreadable": 0 },
176
+ "recon": { "filesScanned": 116, "filesUnreadable": 0 },
177
+ "vendor": { "filesScanned": 0, "filesUnreadable": 0 },
178
+ "opaque": { "filesScanned": 83, "filesUnreadable": 0 },
179
+ "web": { "filesScanned": 9, "filesUnreadable": 0 },
180
+ "compile": { "filesScanned": 83, "filesUnreadable": 0 },
181
+ "priv": { "filesScanned": 234, "filesUnreadable": 0 },
182
+ "log": { "filesScanned": 13, "filesUnreadable": 0 }
183
+ },
184
+ "coverageIncomplete": false,
185
+ "setsIncomplete": [
186
+ { "set": "flow", "kind": "configuration",
187
+ "why": "6 EXEC CICS WRITEQ TD statement(s) write to an extrapartition queue, and nothing says whether it reaches the internal reader: name the region's INTRDR DDs as internalReaderDds (or the queues as internalReaderQueues) in cobolwork.site.json" },
188
+ { "set": "jcl", "kind": "configuration",
189
+ "why": "5 FTP transfer(s) send a named dataset, and cobolwork.site.json names no production qualifier, so the production-data rule did not run on them" },
190
+ { "set": "recon", "kind": "configuration",
191
+ "why": "no cobolwork.site.json: the production-name and production-dataset rules did not run, because nothing declares what production means in this estate" }
192
+ ],
193
+ "flowModel": "byte-range",
194
+ "toolVersion": "0.2.0"
195
+ }
196
+ ```
197
+
198
+ `coverageIncomplete` is the field to read first. An unresolved copybook, an unreadable file or a
199
+ symlink leading out of the tree sets it, because a finding count over source nobody read is not a
200
+ clean result.
201
+
202
+ Some rules need facts no repository holds: which dataset qualifiers are production, which DDs reach
203
+ the internal reader, the compiler options and runtime versions in use, which libraries are
204
+ authorised. They go in `cobolwork.site.json`, and `node diag/propose-site.mjs <path>` drafts one from
205
+ the estate's own JCL for a person to correct. Without a fact, the rule that needs it says it did not
206
+ run, under `setsIncomplete`, rather than reporting a clean result. The benchmark tree has no site
207
+ file, which is why three sets say so above. `advisoryCoverage` names the products the advisory rules
208
+ searched, so a scan with no advisory finding says what that silence covers.
209
+
210
+ ### Findings someone has already judged
211
+
212
+ $ cobolwork baseline . --reason "replaced by a fixed command table in Q1" --who jsmith --expires 2027-03-31
213
+
214
+ writes `cobolwork.baseline.json`, one entry per finding keyed by fingerprint: `accept`,
215
+ `false-positive` or `wont-fix`, with the reason, who, and until when. A later scan moves what it
216
+ covers into `suppressed`. Every suppression expires, and the finding comes back when it does. A
217
+ baseline inside the scanned tree cannot hide tampering - a hidden payload, or an instruction aimed
218
+ at an AI reader - so that takes `--baseline <file>` from outside the tree. `--no-baseline` applies
219
+ none.
220
+
221
+ ## Stopping a build
222
+
223
+ `cobolwork build` is a CI step, and its exit status is its verdict: 0 pass, 1 fail, 3 undecided, 4 the
224
+ compiler failed after a pass, 2 it could not run. No model is asked anything; every check is the
225
+ engine's reading of the tree.
226
+
227
+ cobolwork build . --base origin/main -- cobc -x -o payroll PAYROLL.cbl
228
+
229
+ By default HIGH, CRIT and KNOWN-EXPLOITABLE findings block, and so does any finding, at any tier,
230
+ that would let its author escalate privilege or change data: input choosing a command, a program, an
231
+ SQL statement or a job, or choosing which record is rewritten or where in storage a write lands. MED
232
+ and LOW findings outside those two classes are reported and do not block. With `--base`, a finding
233
+ blocks only if the change introduced it, except CRIT and KNOWN-EXPLOITABLE, which block wherever they
234
+ are; the change is judged by the policy, waivers and site file of its base, so it cannot relax its own
235
+ gate. An incomplete scan is undecided, never a pass.
236
+
237
+ The compiler options are held to the policy too. A program compiled without `SSRANGE`, or with
238
+ `SSRANGE(MSG)`, which reports a bad subscript and carries on, fails; for `cobc` the missing `-fec`
239
+ checks are added to the command. `cobolwork.policy.json` changes any of this, and `--policy <file>`
240
+ names an organisation's floor, which a repository can tighten and never loosen.
241
+ [docs/spec/build-gate.md](docs/spec/build-gate.md) is the full specification.
242
+
243
+ ## How accurate it is
244
+
245
+ The parser is graded against GnuCOBOL's own listing (`cobc -t -Xref -ftsymbols`), which reports every
246
+ data item with the size the compiler computed, every label, called programs, and which references
247
+ write to a field. `diag/grade-against-gnucobol.mjs` runs that comparison over a corpus, on
248
+ repositories never used while building the parser. The 100 and 300 sets were measured 2026-09-18,
249
+ the 500 set 2026-09-26:
250
+
251
+ | Corpus | Files the compiler accepted | Data items | Sizes | Labels and calls |
252
+ |---|---|---|---|---|
253
+ | 100 repositories, held out | 489 | 100% recall, 100% precision | 0 disagree of 15,888 | 100% |
254
+ | 300 repositories, held out | 2,210 | 99.9% / 100% | 8 disagree of 94,786 | 100% / 99.7% |
255
+ | 500 repositories, held out | 21,624 | 99.98% / 99.998% | 5,881 disagree of 733,835 | 100% / 100% |
256
+
257
+ On the 500 set, 5,643 of the 5,881 size disagreements come from one repository that vendors a COBOL
258
+ research dataset; the other repositories disagree on 238 of 343,444. The grade covers only programs
259
+ GnuCOBOL accepts, so a program with EXEC SQL or EXEC CICS is graded only through the precompiler
260
+ stand-in in `diag/precompiler.mjs`, which rewrites what the parser would otherwise have to read. The
261
+ tests compare the parser with the compiler's answers kept in `test/fixtures/parser/*.golden.json`,
262
+ so they run without GnuCOBOL.
263
+
264
+ `bench/cases/` holds 93 CWE-labelled cases, each paired with a near-miss negative: the same shape
265
+ with the flaw removed. `node bench/run.mjs` scores any scanner's findings against them, by rule and
266
+ file, never by line, and `npm test` fails if any case scores differently from its declaration.
267
+ `--validate` compiles every COBOL case with GnuCOBOL and checks every JCL case against the
268
+ statement grammar, a weaker witness, and says so.
269
+
270
+ ## Coverage on a busy machine
271
+
272
+ A scan stops before it exhausts memory, reports how much of the tree it read, and sets
273
+ `coverageIncomplete`. **Read that before the finding count.** On one 4,086-file repository a starved
274
+ run reported 26 findings and a clean one 2,890.
275
+
276
+ The number it watches is the lesser of the heap's headroom and the machine's free memory, and the
277
+ second is the whole machine. On a build agent running other jobs, or in a container whose limit is
278
+ smaller than the host, that means a scan's coverage is decided by what else is running. Say what your
279
+ share is and it will use that instead:
280
+
281
+ ```sh
282
+ COBOLWORK_FREE_MEMORY_MB=4096 cobolwork .
283
+ ```
284
+
285
+ It is a statement, not a limit: the heap is still watched, so an over-generous number does not turn
286
+ the guard off, it just stops the host's load from deciding. A spawned scan inherits this property.
287
+
288
+ ## Compliance
289
+
290
+ Every rule is mapped to the clause of each framework that makes it an obligation, quoted verbatim:
291
+
292
+ | File | Instrument | Rules mapped |
293
+ |---|---|---|
294
+ | `rules/compliance-dora.json` | Regulation (EU) 2022/2554 (DORA) | 156 |
295
+ | `rules/compliance-ffiec.json` | FFIEC IT Examination Handbook | 154, and 2 recorded as unmapped |
296
+ | `rules/compliance-nist80053.json` | NIST SP 800-53 Rev. 5.2.0 | 154, and 2 recorded as unmapped |
297
+
298
+ `node diag/map-compliance.mjs` refuses to write a quote the cached instrument does not contain, and
299
+ every scan carries the mapping as `ruleCompliance`. The clause choice is a judgement that no
300
+ qualified assessor has reviewed. Where no control genuinely covers a rule, as for committing an LPAR
301
+ name to a repository, the rule is recorded as unmapped with a reason rather than mapped to the
302
+ nearest control that reads plausibly. What else the mapping does not claim is in
303
+ [docs/rule-sets.md](docs/rule-sets.md#compliance).
304
+
305
+ ## Tests
306
+
307
+ npm test
308
+
309
+ Tests that need a tool which is absent record a skip naming it. A skipped check is not a passing one.
310
+ Open work is in [BACKLOG.md](BACKLOG.md).
311
+
312
+ ## Security
313
+
314
+ Vulnerabilities in cobolwork itself go through [SECURITY.md](SECURITY.md), privately. A false
315
+ positive or a missed finding is an ordinary issue, and a wanted one: precision and recall are
316
+ measured and published here, so a report that moves either is the most useful thing you can send.
317
+
318
+ ## Licence
319
+
320
+ AGPL-3.0-or-later. See [LICENSE](LICENSE) and [NOTICE](NOTICE). That covers this project's own work; material belonging to
321
+ others — AWS CardDemo fixtures, IBM interface layouts, quoted regulatory clauses — is listed in
322
+ [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) with its own licence.
323
+
324
+ If your organisation's policy refuses AGPL, [LICENSING.md](LICENSING.md) says what else is available
325
+ and what has to be true first. Running the scanner over your own source triggers nothing in the AGPL
326
+ that unmodified internal use does not already satisfy; that page explains why, which is often the
327
+ whole of the objection.
@@ -0,0 +1,118 @@
1
+ # Third-party notices
2
+
3
+ cobolwork is licensed under AGPL-3.0-or-later (see [`LICENSE`](LICENSE)). That licence covers the
4
+ project's own work. The material below is other people's, and is listed here with its licence and
5
+ where it came from.
6
+
7
+ Full licence texts are in [`licences/`](licences/).
8
+
9
+ ---
10
+
11
+ ## COBOL word lists
12
+
13
+ **Where:** [`lib/words.mjs`](lib/words.mjs), generated from
14
+ [`provenance/words.json`](provenance/words.json).
15
+
16
+ The reserved words, special registers, system names and intrinsic-function names are derived from
17
+ online sources - the ISO/IEC 1989 drafts and each vendor's published reference for its own compiler -
18
+ and validity-checked: every word names the document that attests it, and the test suite refuses a word
19
+ that is unattested or is not a well-formed COBOL word.
20
+
21
+ ## AWS CardDemo BMS maps, copybooks and FTP job — Apache-2.0
22
+
23
+ **Where:**
24
+
25
+ | File | Lines | Origin in AWS CardDemo |
26
+ |---|---|---|
27
+ | [`test/fixtures/bms/COCRDSL.bms`](test/fixtures/bms/COCRDSL.bms) | 160 | `app/bms/COCRDSL.bms` |
28
+ | [`test/fixtures/bms/COSGN00.bms`](test/fixtures/bms/COSGN00.bms) | 213 | `app/bms/COSGN00.bms` |
29
+ | [`test/fixtures/bms/COCRDSL.cpy`](test/fixtures/bms/COCRDSL.cpy) | 204 | `app/cpy-bms/COCRDSL.CPY` |
30
+ | [`test/fixtures/bms/COSGN00.cpy`](test/fixtures/bms/COSGN00.cpy) | 156 | `app/cpy-bms/COSGN00.CPY` |
31
+
32
+ **Origin:** <https://github.com/aws-samples/aws-mainframe-modernization-carddemo>
33
+
34
+ **Copyright:** Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
35
+
36
+ **Licence:** Apache License 2.0 — [`licences/Apache-2.0.txt`](licences/Apache-2.0.txt).
37
+
38
+ Copied unchanged, with Amazon's own licence header preserved in each file. They are test fixtures:
39
+ they are not in the `files` list in `package.json`, so they are not redistributed in the published
40
+ package. They are in the repository.
41
+
42
+ [`test/ftp.test.mjs`](test/ftp.test.mjs) reproduces the job card, FTP step and SYSIN lines of
43
+ CardDemo's `app/jcl/FTPJCL.JCL`, including its sample host addresses and logon, as the shape an FTP
44
+ rule must read.
45
+
46
+ ## IBM interface-block layouts — IBM documentation
47
+
48
+ **Where:** [`lib/words.mjs`](lib/words.mjs) — `EIB_LAYOUT` (29 EXEC interface block fields with their
49
+ PICTURE clauses), `DIB_FIELDS` (10), `SQLCA_FIELDS` (23).
50
+
51
+ **Origin:** IBM CICS TS 6.x "EIB fields", IMS 15.3 "Specifying the DL/I interface block (DIB)", and
52
+ Db2 12 for z/OS "Description of SQLCA fields". Reserved-word and register names in the same file
53
+ also come from the IBM Enterprise COBOL for z/OS 6.4 Language Reference (SC27-8713-03).
54
+
55
+ These are interface facts a program must match to be read correctly — the same names and pictures
56
+ the CICS translator and the precompilers supply. They are transcribed from IBM's manuals, and IBM's
57
+ documentation is IBM's copyright.
58
+
59
+ [`rules/system-layouts.json`](rules/system-layouts.json) holds the SQLCA's elementary fields with
60
+ their offsets and lengths, from Db2 13 for z/OS "Description of SQLCA fields": the field order and
61
+ lengths are IBM's, and the offsets are summed from them to the 136 bytes the page states.
62
+
63
+ ## Compiler option names — IBM documentation and GnuCOBOL's published usage
64
+
65
+ **Where:** [`lib/options.mjs`](lib/options.mjs), recorded with their sources in
66
+ [`provenance/compiler-options.json`](provenance/compiler-options.json).
67
+
68
+ **Origin:** the Enterprise COBOL for z/OS 6.3 and 6.4 documentation for `SSRANGE`, `PARMCHECK` and
69
+ `NUMCHECK`, their abbreviations and suboptions; and the `cobc(1)` manual page for GnuCOBOL 3.2's
70
+ `-fec`, `-fno-ec` and `-debug`. The exception-condition names are attested by ISO drafts in
71
+ [`provenance/words.json`](provenance/words.json).
72
+
73
+ These are names of options: interface facts, as the layouts above are. What each does is written in
74
+ this project's own words, and what each GnuCOBOL option does was established by compiling programs
75
+ with GnuCOBOL 3.2.0 and running them. Nothing was taken from GnuCOBOL's source files, word lists or
76
+ help text, which are GPL-3.0-or-later.
77
+
78
+ ## Regulatory instruments — quoted clauses
79
+
80
+ **Where:** [`rules/compliance-dora.json`](rules/compliance-dora.json),
81
+ [`rules/compliance-nist80053.json`](rules/compliance-nist80053.json),
82
+ [`rules/compliance-ffiec.json`](rules/compliance-ffiec.json), and
83
+ [`feed/fixtures/sources/test-doc.txt`](feed/fixtures/sources/test-doc.txt).
84
+
85
+ About 4.2 KB of verbatim clause text in total, each quote matched against its instrument and carrying
86
+ its source URL and retrieval date:
87
+
88
+ - **DORA** — Regulation (EU) 2022/2554. EU law.
89
+ - **NIST SP 800-53 Rev. 5** — a work of the US Government.
90
+ - **FFIEC IT Examination Handbook** — a work of the US Government.
91
+
92
+ PCI DSS and the COBIT-derived SOX material are deliberately absent: they may not be redistributed,
93
+ so they belong in a licensed feed rather than in this repository. Full instrument texts are never
94
+ committed (`feed/sources/` is ignored); the feed ships citations, not text.
95
+
96
+ ## Vulnerability data
97
+
98
+ **Where:** [`rules/advisories.json`](rules/advisories.json) — about 3.9 KB of CVE description text
99
+ across 15 advisories, retrieved from the NVD (a NIST publication; CVE List content is published by
100
+ MITRE under CC0). [`rules/kev-ids.json`](rules/kev-ids.json) — 1,716 CVE identifiers from CISA's
101
+ Known Exploited Vulnerabilities catalogue, identifiers only, no CISA prose.
102
+
103
+ ## Contributor agreement
104
+
105
+ [`CLA.md`](CLA.md) is adapted from the Apache Software Foundation's Individual Contributor License
106
+ Agreement v2.0. The adaptation is disclosed in the document.
107
+
108
+ ## Trademarks
109
+
110
+ CICS, IMS, Db2, z/OS and Enterprise COBOL are trademarks of IBM. ACF2, Top Secret and Connect:Direct
111
+ are trademarks of Broadcom. Control-M is a trademark of BMC. GnuCOBOL is a GNU project. All are used
112
+ nominatively, to say what this software reads and which products a rule concerns. No affiliation or
113
+ endorsement is claimed.
114
+
115
+ ---
116
+
117
+ Something missing or wrong here is a bug: please report it the way [`SECURITY.md`](SECURITY.md)
118
+ describes for anything sensitive, or open an issue otherwise.