@portll/cobolwork 0.0.1 → 0.2.75
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.
- package/LICENSE +661 -0
- package/LICENSING.md +93 -0
- package/NOTICE +9 -0
- package/README.md +323 -3
- package/THIRD-PARTY-NOTICES.md +118 -0
- package/bin/cobolwork.mjs +354 -0
- package/lib/advisories.mjs +133 -0
- package/lib/baseline.mjs +154 -0
- package/lib/bms.mjs +453 -0
- package/lib/build.mjs +402 -0
- package/lib/capabilities.mjs +79 -0
- package/lib/cics-commands.mjs +281 -0
- package/lib/compliance.mjs +81 -0
- package/lib/consequence.mjs +139 -0
- package/lib/control.mjs +1515 -0
- package/lib/csd.mjs +77 -0
- package/lib/dataflow.mjs +1506 -0
- package/lib/diff.mjs +344 -0
- package/lib/explain.mjs +145 -0
- package/lib/gate.mjs +383 -0
- package/lib/index.mjs +6 -0
- package/lib/inventory.mjs +79 -0
- package/lib/jcl.mjs +478 -0
- package/lib/kernel/findings.mjs +94 -0
- package/lib/kernel/identity.mjs +216 -0
- package/lib/kernel/memory.mjs +217 -0
- package/lib/kernel/printable.mjs +6 -0
- package/lib/kernel/registry.mjs +79 -0
- package/lib/kernel/ruleset.mjs +72 -0
- package/lib/kernel/source-tree.mjs +159 -0
- package/lib/kev.mjs +27 -0
- package/lib/options.mjs +512 -0
- package/lib/packs.mjs +148 -0
- package/lib/parser.mjs +2055 -0
- package/lib/policy.mjs +163 -0
- package/lib/precompile-cics.mjs +169 -0
- package/lib/precompile.mjs +544 -0
- package/lib/reach.mjs +122 -0
- package/lib/revision.json +1 -0
- package/lib/revision.mjs +89 -0
- package/lib/sarif.mjs +222 -0
- package/lib/scan.mjs +272 -0
- package/lib/sets/build.mjs +234 -0
- package/lib/sets/cics.mjs +306 -0
- package/lib/sets/compile.mjs +187 -0
- package/lib/sets/copybook.mjs +174 -0
- package/lib/sets/flow.mjs +487 -0
- package/lib/sets/hidden.mjs +216 -0
- package/lib/sets/jcl.mjs +440 -0
- package/lib/sets/log.mjs +406 -0
- package/lib/sets/opaque.mjs +102 -0
- package/lib/sets/priv.mjs +322 -0
- package/lib/sets/recon.mjs +267 -0
- package/lib/sets/vendor.mjs +117 -0
- package/lib/sets/web.mjs +327 -0
- package/lib/site.mjs +164 -0
- package/lib/sources.mjs +156 -0
- package/lib/tui/app.mjs +325 -0
- package/lib/tui/keys.mjs +39 -0
- package/lib/tui/model.mjs +96 -0
- package/lib/tui/run.mjs +38 -0
- package/lib/tui/screen.mjs +59 -0
- package/lib/tui/terminal.mjs +46 -0
- package/lib/utilities.mjs +296 -0
- package/lib/version.mjs +15 -0
- package/lib/words.mjs +318 -0
- package/package.json +45 -6
- package/rules/advisories.json +264 -0
- package/rules/compliance-dora.json +2151 -0
- package/rules/compliance-ffiec.json +2134 -0
- package/rules/compliance-nist80053.json +2134 -0
- package/rules/gitleaks-mainframe.toml +57 -0
- package/rules/kev-ids.json +1729 -0
- package/rules/packs/broadcom.json +124 -0
- package/rules/packs/connectdirect.json +116 -0
- package/rules/packs/controlm.json +114 -0
- package/rules/system-layouts.json +28 -0
- package/schema/cobolwork-coverage.schema.json +65 -0
- 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,325 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cobolwork
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/Portll/cobolwork/actions/workflows/ci.yml)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](package.json)
|
|
6
|
+
[](LICENSE)
|
|
4
7
|
|
|
5
|
-
|
|
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
|
+
|
|
27
|
+
The command it installs is `cobolwork`. The same package is attached to each
|
|
28
|
+
[release](https://github.com/Portll/cobolwork/releases) as `cobolwork-<version>.tgz`, which is the
|
|
29
|
+
one to pin, and as `cobolwork.tgz`, which
|
|
30
|
+
`npm install -g https://github.com/Portll/cobolwork/releases/latest/download/cobolwork.tgz`
|
|
31
|
+
follows. To run from a checkout instead:
|
|
32
|
+
|
|
33
|
+
git clone https://github.com/Portll/cobolwork && cd cobolwork && npm link
|
|
34
|
+
|
|
35
|
+
Node 18 or later. No dependencies, runtime or development. `npm link` puts `cobolwork` on your PATH;
|
|
36
|
+
adding `bin/` to PATH does the same thing. GnuCOBOL is needed only to regrade the parser or validate
|
|
37
|
+
benchmark cases.
|
|
38
|
+
|
|
39
|
+
## What it does
|
|
40
|
+
|
|
41
|
+
| Command | Information |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `cobolwork scan <path>` | every rule set below, as JSON or SARIF |
|
|
44
|
+
| `cobolwork inventory <path>` | inventory (and what couldn't be read) |
|
|
45
|
+
| `cobolwork flow <path>` | where untrusted data reaches a sensitive operation, with the path it took |
|
|
46
|
+
| `cobolwork diff <repo> --base <ref>` | what a change reaches: layouts it moves in programs nobody edited, new call targets, findings it adds or removes |
|
|
47
|
+
| `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 |
|
|
48
|
+
| `cobolwork parse <file>` | one file's structure, for debugging |
|
|
49
|
+
|
|
50
|
+
## What it finds
|
|
51
|
+
|
|
52
|
+
| Area | What is reported |
|
|
53
|
+
|---|---|
|
|
54
|
+
| 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 |
|
|
55
|
+
| Checks | a check counts only where it runs first, on every route; one that leaves the value safe for the sink clears the route |
|
|
56
|
+
| Screen fields | a field the BMS map protects, read back and used to choose a record: the 3270 hidden form field |
|
|
57
|
+
| Exfiltration | database rows and file records leaving through web calls, sockets, MQ, extrapartition queues or service calls |
|
|
58
|
+
| 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 |
|
|
59
|
+
| 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 |
|
|
60
|
+
| 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 |
|
|
61
|
+
| The source | names nothing declares (code that cannot compile), shadowed copybooks, payloads hidden in columns 73-80 or aimed at AI readers |
|
|
62
|
+
| The estate | production names outside production jobs, routable addresses, compiler and runtime versions with published advisories |
|
|
63
|
+
|
|
64
|
+
[docs/rule-sets.md](docs/rule-sets.md) describes each in full, with what it deliberately leaves out.
|
|
65
|
+
Vendor packs for CA ACF2 and Top Secret, Control-M and Connect:Direct load only for estates that
|
|
66
|
+
name them, and `rules/gitleaks-mainframe.toml` gives gitleaks the mainframe credential shapes it
|
|
67
|
+
lacks.
|
|
68
|
+
|
|
69
|
+
## What a finding looks like
|
|
70
|
+
|
|
71
|
+
The flaw is in neither file on its own: one program reads the command line and hands it on, the
|
|
72
|
+
other runs what it was handed. Both commands below run against this repository, so every number
|
|
73
|
+
here can be checked.
|
|
74
|
+
|
|
75
|
+
$ cobolwork flow test/fixtures/dataflow
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"rule": "argv-or-env-to-os-command",
|
|
80
|
+
"sev": "crit",
|
|
81
|
+
"cwe": "CWE-78",
|
|
82
|
+
"path": "pos/P2.cbl",
|
|
83
|
+
"line": 10,
|
|
84
|
+
"program": "P2",
|
|
85
|
+
"crossProgram": true,
|
|
86
|
+
"hops": 4,
|
|
87
|
+
"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",
|
|
88
|
+
"trace": [
|
|
89
|
+
{ "program": "P1", "item": "WS-IN", "file": "pos/P1.cbl", "via": "source" },
|
|
90
|
+
{ "program": "P1", "item": "WS-CMD", "file": "pos/P1.cbl", "via": "MOVE at pos/P1.cbl:9" },
|
|
91
|
+
{ "program": "P2", "item": "LK-CMD", "file": "pos/P2.cbl", "via": "CALL 'P2' argument 1 at pos/P1.cbl:10" },
|
|
92
|
+
{ "program": "P2", "item": "WS-LOCAL", "file": "pos/P2.cbl", "via": "MOVE at pos/P2.cbl:9" }
|
|
93
|
+
],
|
|
94
|
+
"related": [{ "path": "pos/P1.cbl", "line": 8, "detail": "ACCEPT ... FROM COMMAND-LINE" }],
|
|
95
|
+
"sources": 1,
|
|
96
|
+
"evidence": "path",
|
|
97
|
+
"fingerprint": "45e55b502c397a499cc29212c0c28329"
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`crossProgram` marks a path that left the file it started in. Findings carry no source text, so a
|
|
102
|
+
report can be stored and passed on without carrying the code with it.
|
|
103
|
+
|
|
104
|
+
`fingerprint` is what the finding is, rather than where it is printed today: the rule, the program
|
|
105
|
+
and the paragraph or section it sits in (the job, step and DD for JCL), and the flagged statement's
|
|
106
|
+
own text. No line number goes into it, so code added above a finding does not change it. `diff`
|
|
107
|
+
compares findings by it, and SARIF carries it as `partialFingerprints["cobolwork/v1"]`. Two findings
|
|
108
|
+
that only their position tells apart share one, and `summary.identity.shared` counts them.
|
|
109
|
+
|
|
110
|
+
### Findings and Claim Severity
|
|
111
|
+
|
|
112
|
+
Severity says how urgent a finding is. `evidence` says what the tool actually established, which
|
|
113
|
+
decides who acts on it. Every rule declares one of eight finding types:
|
|
114
|
+
|
|
115
|
+
| `evidence` | What the finding claims | Who acts |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `path` | untrusted input was traced to a sensitive operation, and `trace` is the route | the program's owner |
|
|
118
|
+
| `construct` | the construct is a defect wherever it sits; no input has to reach it | the owner, or whoever can rotate a credential |
|
|
119
|
+
| `tampering` | the source is arranged so a reader or resolver sees something other than what runs | a reviewer, before merge |
|
|
120
|
+
| `advisory` | a pinned compiler or runtime matches a published advisory | whoever owns the build |
|
|
121
|
+
| `exposure` | information about the estate is written into source | the owner |
|
|
122
|
+
| `change` | a change moves an interface or adds a call target (`diff` only) | the reviewer of that change |
|
|
123
|
+
| `coverage` | the analysis stopped following here | nobody's code; read more, or accept the limit |
|
|
124
|
+
| `context` | describes the estate (an entry point, a product in use) | nobody; it asserts no defect |
|
|
125
|
+
|
|
126
|
+
`coverage` and `context` are exactly the `info` rules (not defects). A consumer that counts these
|
|
127
|
+
findings should leave them out of its counts.
|
|
128
|
+
|
|
129
|
+
None of the kinds says *exploitable*, on purpose. Whether a route can be used also depends on who may
|
|
130
|
+
start the transaction or job that reaches it, and on what the running system enforces, and no
|
|
131
|
+
repository holds those facts. A finding names the entry points that reach it (`startedBy`). `path`
|
|
132
|
+
is the strongest claim the tool makes: a route found by reading the code, not by running it. Its
|
|
133
|
+
precision has been measured on benchmark cases this project wrote, not yet on an independently
|
|
134
|
+
labelled corpus.
|
|
135
|
+
|
|
136
|
+
### What each finding lets someone do, and the fix
|
|
137
|
+
|
|
138
|
+
A defect finding carries two more facts, the same for every finding of its rule: what someone can do
|
|
139
|
+
once the route or construct is present, and the standard fix. They are what makes a `path` or
|
|
140
|
+
`construct` finding read as a hole to act on rather than a location. A report carries them once per
|
|
141
|
+
rule, keyed by rule id beside `ruleText` and `ruleCwe`, so a finding does not repeat them. For the
|
|
142
|
+
finding above:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
"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" },
|
|
146
|
+
"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" }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The `who` is the finding's own — the entry points it carries in `startedBy` — and the impact
|
|
150
|
+
completes it. SARIF puts the impact in each rule's `fullDescription` and the fix in its `help`, which
|
|
151
|
+
is where GitHub code scanning shows a recommendation. The `info` kinds carry neither, because a fix
|
|
152
|
+
would assert a defect they do not claim; a site-gated rule carries them once the estate's fact turns
|
|
153
|
+
it into a defect, and not before.
|
|
154
|
+
|
|
155
|
+
### What it could not read
|
|
156
|
+
|
|
157
|
+
A scan says what each rule set read, not only what it found:
|
|
158
|
+
|
|
159
|
+
$ cobolwork scan bench/cases --quiet
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
"summary": {
|
|
163
|
+
"findings": 127,
|
|
164
|
+
"bySeverity": { "high": 28, "crit": 16, "med": 16, "low": 9, "info": 58 },
|
|
165
|
+
"byEvidence": { "tampering": 6, "path": 31, "advisory": 1, "construct": 31, "coverage": 6, "context": 52 },
|
|
166
|
+
"bySet": {
|
|
167
|
+
"inventory": { "filesScanned": 83, "filesUnreadable": 0 },
|
|
168
|
+
"flow": { "filesScanned": 83, "filesUnreadable": 0 },
|
|
169
|
+
"cics": { "filesScanned": 42, "filesUnreadable": 0 },
|
|
170
|
+
"hidden": { "filesScanned": 116, "filesUnreadable": 0 },
|
|
171
|
+
"copybook": { "filesScanned": 88, "filesUnreadable": 0 },
|
|
172
|
+
"jcl": { "filesScanned": 28, "filesUnreadable": 0 },
|
|
173
|
+
"build": { "filesScanned": 2, "filesUnreadable": 0 },
|
|
174
|
+
"recon": { "filesScanned": 116, "filesUnreadable": 0 },
|
|
175
|
+
"vendor": { "filesScanned": 0, "filesUnreadable": 0 },
|
|
176
|
+
"opaque": { "filesScanned": 83, "filesUnreadable": 0 },
|
|
177
|
+
"web": { "filesScanned": 9, "filesUnreadable": 0 },
|
|
178
|
+
"compile": { "filesScanned": 83, "filesUnreadable": 0 },
|
|
179
|
+
"priv": { "filesScanned": 234, "filesUnreadable": 0 },
|
|
180
|
+
"log": { "filesScanned": 13, "filesUnreadable": 0 }
|
|
181
|
+
},
|
|
182
|
+
"coverageIncomplete": false,
|
|
183
|
+
"setsIncomplete": [
|
|
184
|
+
{ "set": "flow", "kind": "configuration",
|
|
185
|
+
"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" },
|
|
186
|
+
{ "set": "jcl", "kind": "configuration",
|
|
187
|
+
"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" },
|
|
188
|
+
{ "set": "recon", "kind": "configuration",
|
|
189
|
+
"why": "no cobolwork.site.json: the production-name and production-dataset rules did not run, because nothing declares what production means in this estate" }
|
|
190
|
+
],
|
|
191
|
+
"flowModel": "byte-range",
|
|
192
|
+
"toolVersion": "0.2.0"
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`coverageIncomplete` is the field to read first. An unresolved copybook, an unreadable file or a
|
|
197
|
+
symlink leading out of the tree sets it, because a finding count over source nobody read is not a
|
|
198
|
+
clean result.
|
|
199
|
+
|
|
200
|
+
Some rules need facts no repository holds: which dataset qualifiers are production, which DDs reach
|
|
201
|
+
the internal reader, the compiler options and runtime versions in use, which libraries are
|
|
202
|
+
authorised. They go in `cobolwork.site.json`, and `node diag/propose-site.mjs <path>` drafts one from
|
|
203
|
+
the estate's own JCL for a person to correct. Without a fact, the rule that needs it says it did not
|
|
204
|
+
run, under `setsIncomplete`, rather than reporting a clean result. The benchmark tree has no site
|
|
205
|
+
file, which is why three sets say so above. `advisoryCoverage` names the products the advisory rules
|
|
206
|
+
searched, so a scan with no advisory finding says what that silence covers.
|
|
207
|
+
|
|
208
|
+
### Findings someone has already judged
|
|
209
|
+
|
|
210
|
+
$ cobolwork baseline . --reason "replaced by a fixed command table in Q1" --who jsmith --expires 2027-03-31
|
|
211
|
+
|
|
212
|
+
writes `cobolwork.baseline.json`, one entry per finding keyed by fingerprint: `accept`,
|
|
213
|
+
`false-positive` or `wont-fix`, with the reason, who, and until when. A later scan moves what it
|
|
214
|
+
covers into `suppressed`. Every suppression expires, and the finding comes back when it does. A
|
|
215
|
+
baseline inside the scanned tree cannot hide tampering - a hidden payload, or an instruction aimed
|
|
216
|
+
at an AI reader - so that takes `--baseline <file>` from outside the tree. `--no-baseline` applies
|
|
217
|
+
none.
|
|
218
|
+
|
|
219
|
+
## Stopping a build
|
|
220
|
+
|
|
221
|
+
`cobolwork build` is a CI step, and its exit status is its verdict: 0 pass, 1 fail, 3 undecided, 4 the
|
|
222
|
+
compiler failed after a pass, 2 it could not run. No model is asked anything; every check is the
|
|
223
|
+
engine's reading of the tree.
|
|
224
|
+
|
|
225
|
+
cobolwork build . --base origin/main -- cobc -x -o payroll PAYROLL.cbl
|
|
226
|
+
|
|
227
|
+
By default HIGH, CRIT and KNOWN-EXPLOITABLE findings block, and so does any finding, at any tier,
|
|
228
|
+
that would let its author escalate privilege or change data: input choosing a command, a program, an
|
|
229
|
+
SQL statement or a job, or choosing which record is rewritten or where in storage a write lands. MED
|
|
230
|
+
and LOW findings outside those two classes are reported and do not block. With `--base`, a finding
|
|
231
|
+
blocks only if the change introduced it, except CRIT and KNOWN-EXPLOITABLE, which block wherever they
|
|
232
|
+
are; the change is judged by the policy, waivers and site file of its base, so it cannot relax its own
|
|
233
|
+
gate. An incomplete scan is undecided, never a pass.
|
|
234
|
+
|
|
235
|
+
The compiler options are held to the policy too. A program compiled without `SSRANGE`, or with
|
|
236
|
+
`SSRANGE(MSG)`, which reports a bad subscript and carries on, fails; for `cobc` the missing `-fec`
|
|
237
|
+
checks are added to the command. `cobolwork.policy.json` changes any of this, and `--policy <file>`
|
|
238
|
+
names an organisation's floor, which a repository can tighten and never loosen.
|
|
239
|
+
[docs/spec/build-gate.md](docs/spec/build-gate.md) is the full specification.
|
|
240
|
+
|
|
241
|
+
## How accurate it is
|
|
242
|
+
|
|
243
|
+
The parser is graded against GnuCOBOL's own listing (`cobc -t -Xref -ftsymbols`), which reports every
|
|
244
|
+
data item with the size the compiler computed, every label, called programs, and which references
|
|
245
|
+
write to a field. `diag/grade-against-gnucobol.mjs` runs that comparison over a corpus, on
|
|
246
|
+
repositories never used while building the parser. The 100 and 300 sets were measured 2026-09-18,
|
|
247
|
+
the 500 set 2026-09-26:
|
|
248
|
+
|
|
249
|
+
| Corpus | Files the compiler accepted | Data items | Sizes | Labels and calls |
|
|
250
|
+
|---|---|---|---|---|
|
|
251
|
+
| 100 repositories, held out | 489 | 100% recall, 100% precision | 0 disagree of 15,888 | 100% |
|
|
252
|
+
| 300 repositories, held out | 2,210 | 99.9% / 100% | 8 disagree of 94,786 | 100% / 99.7% |
|
|
253
|
+
| 500 repositories, held out | 21,624 | 99.98% / 99.998% | 5,881 disagree of 733,835 | 100% / 100% |
|
|
254
|
+
|
|
255
|
+
On the 500 set, 5,643 of the 5,881 size disagreements come from one repository that vendors a COBOL
|
|
256
|
+
research dataset; the other repositories disagree on 238 of 343,444. The grade covers only programs
|
|
257
|
+
GnuCOBOL accepts, so a program with EXEC SQL or EXEC CICS is graded only through the precompiler
|
|
258
|
+
stand-in in `diag/precompiler.mjs`, which rewrites what the parser would otherwise have to read. The
|
|
259
|
+
tests compare the parser with the compiler's answers kept in `test/fixtures/parser/*.golden.json`,
|
|
260
|
+
so they run without GnuCOBOL.
|
|
261
|
+
|
|
262
|
+
`bench/cases/` holds 93 CWE-labelled cases, each paired with a near-miss negative: the same shape
|
|
263
|
+
with the flaw removed. `node bench/run.mjs` scores any scanner's findings against them, by rule and
|
|
264
|
+
file, never by line, and `npm test` fails if any case scores differently from its declaration.
|
|
265
|
+
`--validate` compiles every COBOL case with GnuCOBOL and checks every JCL case against the
|
|
266
|
+
statement grammar, a weaker witness, and says so.
|
|
267
|
+
|
|
268
|
+
## Coverage on a busy machine
|
|
269
|
+
|
|
270
|
+
A scan stops before it exhausts memory, reports how much of the tree it read, and sets
|
|
271
|
+
`coverageIncomplete`. **Read that before the finding count.** On one 4,086-file repository a starved
|
|
272
|
+
run reported 26 findings and a clean one 2,890.
|
|
273
|
+
|
|
274
|
+
The number it watches is the lesser of the heap's headroom and the machine's free memory, and the
|
|
275
|
+
second is the whole machine. On a build agent running other jobs, or in a container whose limit is
|
|
276
|
+
smaller than the host, that means a scan's coverage is decided by what else is running. Say what your
|
|
277
|
+
share is and it will use that instead:
|
|
278
|
+
|
|
279
|
+
```sh
|
|
280
|
+
COBOLWORK_FREE_MEMORY_MB=4096 cobolwork .
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
It is a statement, not a limit: the heap is still watched, so an over-generous number does not turn
|
|
284
|
+
the guard off, it just stops the host's load from deciding. A spawned scan inherits this property.
|
|
285
|
+
|
|
286
|
+
## Compliance
|
|
287
|
+
|
|
288
|
+
Every rule is mapped to the clause of each framework that makes it an obligation, quoted verbatim:
|
|
289
|
+
|
|
290
|
+
| File | Instrument | Rules mapped |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `rules/compliance-dora.json` | Regulation (EU) 2022/2554 (DORA) | 156 |
|
|
293
|
+
| `rules/compliance-ffiec.json` | FFIEC IT Examination Handbook | 154, and 2 recorded as unmapped |
|
|
294
|
+
| `rules/compliance-nist80053.json` | NIST SP 800-53 Rev. 5.2.0 | 154, and 2 recorded as unmapped |
|
|
295
|
+
|
|
296
|
+
`node diag/map-compliance.mjs` refuses to write a quote the cached instrument does not contain, and
|
|
297
|
+
every scan carries the mapping as `ruleCompliance`. The clause choice is a judgement that no
|
|
298
|
+
qualified assessor has reviewed. Where no control genuinely covers a rule, as for committing an LPAR
|
|
299
|
+
name to a repository, the rule is recorded as unmapped with a reason rather than mapped to the
|
|
300
|
+
nearest control that reads plausibly. What else the mapping does not claim is in
|
|
301
|
+
[docs/rule-sets.md](docs/rule-sets.md#compliance).
|
|
302
|
+
|
|
303
|
+
## Tests
|
|
304
|
+
|
|
305
|
+
npm test
|
|
306
|
+
|
|
307
|
+
Tests that need a tool which is absent record a skip naming it. A skipped check is not a passing one.
|
|
308
|
+
Open work is in [BACKLOG.md](BACKLOG.md).
|
|
309
|
+
|
|
310
|
+
## Security
|
|
311
|
+
|
|
312
|
+
Vulnerabilities in cobolwork itself go through [SECURITY.md](SECURITY.md), privately. A false
|
|
313
|
+
positive or a missed finding is an ordinary issue, and a wanted one: precision and recall are
|
|
314
|
+
measured and published here, so a report that moves either is the most useful thing you can send.
|
|
315
|
+
|
|
316
|
+
## Licence
|
|
317
|
+
|
|
318
|
+
AGPL-3.0-or-later. See [LICENSE](LICENSE) and [NOTICE](NOTICE). That covers this project's own work; material belonging to
|
|
319
|
+
others — AWS CardDemo fixtures, IBM interface layouts, quoted regulatory clauses — is listed in
|
|
320
|
+
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) with its own licence.
|
|
321
|
+
|
|
322
|
+
If your organisation's policy refuses AGPL, [LICENSING.md](LICENSING.md) says what else is available
|
|
323
|
+
and what has to be true first. Running the scanner over your own source triggers nothing in the AGPL
|
|
324
|
+
that unmodified internal use does not already satisfy; that page explains why, which is often the
|
|
325
|
+
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.
|