-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy paththreat-model.yaml
More file actions
333 lines (312 loc) · 19.5 KB
/
Copy paththreat-model.yaml
File metadata and controls
333 lines (312 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Machine-readable companion to THREAT_MODEL.md (see its section 14).
# Lets scanning/triage tooling auto-classify findings against Groovy's
# security scope. THREAT_MODEL.md is authoritative; this file mirrors it.
# Section anchors (e.g. "THREAT_MODEL.md#3") are given for every entry.
schema: groovy-threat-model/v1
project: Apache Groovy
document: THREAT_MODEL.md
status: draft-for-pmc-review
applies_to: ["3.0.x", "4.0.x", "5.0.x", "6.0.x-alpha"]
# The single decisive rule. See THREAT_MODEL.md#1 and #4.
core_principle: >-
Groovy is a general-purpose programming and scripting language. Running
the code it is given (filesystem, network, process, reflection) is the
product, not a vulnerability. The trust boundary is the source, scripts,
templates, and objects the embedding application chooses to compile,
evaluate, or deserialize. Groovy is not a sandbox.
core_principle_provenance: maintainer # THREAT_MODEL.md#1 / #4
provenance_tags:
documented: backed by shipped docs/javadoc/source verified in-repo
inferred: reasoned position, not yet confirmed by the PMC
maintainer: confirmed by the PMC through approval of this document
roles: # THREAT_MODEL.md#2
- id: developer
trust: trusted
note: authors source/scripts/templates/classpath/AST-transforms; full JVM authority by design
- id: build-operator
trust: trusted
note: controls what is compiled and the build environment
- id: cli-user
trust: trusted-local
note: runs groovy/groovyc/groovysh/groovyConsole with own privileges on own files
- id: end-user
trust: untrusted
note: supplies DATA to an app built with Groovy; supplies CODE only if the developer chose to evaluate it
# Narrow, default-on conveniences. A default-config violation is VALID.
# THREAT_MODEL.md#8
properties:
- id: P1
title: Parameterized SQL for the GString form
cwe: CWE-89
condition: groovy.sql.Sql GString/bind APIs used (not a hand-concatenated String)
violation: a GString placeholder reaching the driver as literal SQL
severity_if_violated: High
provenance: documented
- id: P2
title: Hardened XML parsing by default
cwe: [CWE-611, CWE-776]
condition: allowDocTypeDeclaration left at default false (XmlParser/XmlSlurper/XmlUtil/FactorySupport)
violation: default-configured parsing resolving an external entity or expanding a DTD bomb
severity_if_violated: High
provenance: documented
- id: P3
title: Data-only structured-data parsing (JSON, YAML, TOML, CSV)
cwe: CWE-502
condition: untyped parse() used (all JsonParserType variants; YAML/TOML/CSV via Jackson untyped binding, YAML uses YamlConverter not SnakeYAML Yaml.load); parseAs(Class,..) binds a caller-supplied type with no default typing
violation: a document causing instantiation of a class it names
severity_if_violated: Critical
scope_note: type-safety guarantee only, not resource-exhaustion; DoS robustness is addressed separately and now depth-bounded by default for all of them, incl. a JsonSlurper nesting-depth cap from 6.0.0 (groovy.json.maxNestingDepth, GROOVY-12064), THREAT_MODEL.md#6; TomlSlurper/CsvSlurper are @Incubating
provenance: documented # verified in groovy-json, groovy-yaml YamlConverter, groovy-toml, groovy-csv
- id: P4
title: Owner-only temporary artifacts
cwe: [CWE-377, CWE-378]
condition: Groovy-created temp dirs via NIO Files.createTempDirectory
violation: a Groovy-created temp artifact being world-readable/writable
severity_if_violated: "Medium (local)"
provenance: documented # CVE-2020-17521 fix class
- id: P5
title: Coordinated security maintenance
condition: supported branches per .github/SECURITY.md
provenance: documented
# Looks like security, is not. THREAT_MODEL.md#9
false_friends:
- SecureASTCustomizer # grammar/AST filter, explicitly not a complete solution; bypassable
- "@ThreadInterrupt" # cooperative runaway-script guard, not a malice boundary
- "@TimedInterrupt"
- "@ConditionInterrupt"
- "@CompileStatic" # correctness/perf, not security
- "@TypeChecked"
- groovy.sql.Sql-non-GString # only GString/bind forms parameterize (P1); Sql.expand & DataSet table/column names are identifiers, not protected
- groovysh / groovyConsole # run with user's full privileges; not sandboxed
not_provided: # THREAT_MODEL.md#9
- a sandbox / isolation of executed code
- protection against malicious source, scripts, templates, or serialized objects
- SecurityManager-based confinement (deprecated/removed in the JDK)
- defense against app-built command/SQL/URL/path/option injection, ReDoS, algorithmic-complexity (hash-flooding) DoS, or unbounded-input DoS
# Trust assumptions about the host/environment. THREAT_MODEL.md#5
environment_assumptions:
- id: jvm-trusted
note: the hosting JVM/JDK is trusted and adequately patched; Groovy inherits its security posture (incl. SecurityManager removal)
provenance: documented
- id: memory-safety
note: Groovy inherits the JVM's memory safety; memory-corruption classes (buffer overflow, use-after-free, native type confusion) are out of model — a JDK/JVM bug, not Groovy's
provenance: documented
- id: release-integrity
note: official releases are GPG-signed/checksummed (ASF process); the build pins deps via Gradle dependency verification (gradle/verification-metadata.xml) and publishes a CycloneDX SBOM per module; jar tampering is a supply-chain compromise, out of model (adversary-not-in-scope; see adversary.cannot_be_assumed_to)
provenance: documented
- id: trusted-config
note: classpath, CompilerConfiguration, AST transforms, extension modules, groovy.* properties reflect operator intent, not an attacker's
provenance: inferred
- id: cli-not-a-privilege-boundary
note: CLI tools run with the user's own privileges; not a boundary between users on a host
provenance: inferred
# THREAT_MODEL.md#7 — bounded adversary (closed capability list).
adversary:
in_scope:
- data supplier: bytes to a parser; values bound as data (SQL params, template bindings not template text); oversized/deeply-nested/adversarial data
- local user on a shared host: reads files subject to OS permissions
capabilities_are_a_closed_list: a finding needing a capability not listed is OUT-OF-MODEL (adversary-not-in-scope)
cannot_be_assumed_to:
- supply code/scripts/templates/serialized objects the app evaluates or deserializes
- alter classpath, AST transforms, extension modules, Grape config, CompilerConfiguration, or groovy.* properties
- replace or tamper with the Groovy artifacts themselves (supply-chain)
- attack the JVM/JDK or third-party dependencies rather than Groovy itself
# Every report resolves to exactly one. THREAT_MODEL.md#13
dispositions:
- id: VALID
meaning: violates a provided property (properties[]) in a default configuration
handling: private report per .github/SECURITY.md; fix + advisory
- id: VALID-HARDENING
meaning: >-
by design and not a CVE, but we choose to improve secure-by-default
behavior anyway — defense-in-depth on a reported issue, or a proactive
hardening recognized as good practice
handling: handled in the open, normal priority; not embargoed
- id: "OUT-OF-MODEL: executes-supplied-code"
meaning: relies on the app compiling/evaluating attacker-influenced code or templates
handling: close citing THREAT_MODEL.md#3
- id: "OUT-OF-MODEL: untrusted-deserialization"
meaning: relies on the app deserializing attacker-controlled bytes
handling: close citing THREAT_MODEL.md#3 / #11
- id: "OUT-OF-MODEL: trusted-input"
meaning: relies on attacker control of classpath, config, AST transforms, or groovy.* properties
handling: close citing THREAT_MODEL.md#3
- id: "OUT-OF-MODEL: downstream-responsibility"
meaning: app-built SQL/command/URL/path from untrusted input
handling: close citing THREAT_MODEL.md#10
- id: "OUT-OF-MODEL: equivalent-harm"
meaning: a new path to an effect the actor could already reach legitimately
handling: close citing THREAT_MODEL.md#3
- id: "OUT-OF-MODEL: adversary-not-in-scope"
meaning: requires a capability outside the section-7 closed list (already-privileged actor, supply-chain tampering)
handling: close citing THREAT_MODEL.md#7
- id: "BY-DESIGN: property-disclaimed"
meaning: targets a not_provided / false_friend item (e.g. SecureASTCustomizer bypass)
handling: close citing THREAT_MODEL.md#9
- id: KNOWN-NON-FINDING
meaning: matches known_non_findings[]
handling: close citing the matched row
- id: MODEL-GAP
meaning: a legitimate in-model scenario this model does not yet cover
handling: update the model, then re-triage
# THREAT_MODEL.md#13 — batched scan reports (one document, many findings).
batch_handling:
rule: assign exactly one disposition per candidate finding
enters_private_flow: [VALID, VALID-HARDENING]
embargo_sensitive: [VALID] # VALID-HARDENING is handled in the open
everything_else: closed in-batch with a pointer to the cited section; not an advisory
# Genuine vulns in explicitly-unstable surface stay VALID and can still get a
# CVE, but are handled at reduced priority. THREAT_MODEL.md#8 / #13.
stability_qualifier:
applies_to:
- "@Incubating APIs (any release stream)"
- pre-release / alpha / beta builds
effect: >-
a VALID or VALID-HARDENING finding confined to this surface keeps its
disposition but is best-effort, reduced-priority, and not embargoed-urgent;
it might be handled in the open in some cases. We do not wash our hands of
it — it can still warrant a CVE.
lifted_when: the same vulnerability also reaches stable, non-incubating surface in a supported release
mirrors: .github/SECURITY.md pre-release "(**)" footnote and the @Incubating note
# Patterns a scanner WILL flag in a language implementation; expected,
# not vulnerabilities unless a concrete in-model data->code/object
# boundary crossing is shown. THREAT_MODEL.md#11a
known_non_findings:
- pattern: GroovyClassLoader / parseClass / defineClass / bytecode generation
disposition: KNOWN-NON-FINDING
rationale: the compiler doing its job
- pattern: reflection, MetaClass, invokeMethod/getProperty/setProperty/methodMissing, MethodHandle/invokedynamic
disposition: KNOWN-NON-FINDING
rationale: core Meta-Object Protocol
- pattern: ProcessGroovyMethods / String.execute() / Runtime.exec wrappers
disposition: "OUT-OF-MODEL: downstream-responsibility"
rationale: intended GDK API; injection needs app-supplied untrusted input
- pattern: runtime types (e.g. Closure and friends) implementing Serializable, readObject/readResolve
disposition: "OUT-OF-MODEL: untrusted-deserialization"
rationale: requires deserializing attacker bytes; apply the capability-vs-data test in serialization_policy (THREAT_MODEL.md#11c) — capability primitives are VALID-HARDENING, arbitrary-graph DoS stays out of model
- pattern: ObjectInputStream use in Groovy's own helpers
disposition: "OUT-OF-MODEL: untrusted-deserialization"
rationale: same as above
- pattern: AST transforms executing code at compile time (local & global)
disposition: KNOWN-NON-FINDING
rationale: by design; compile time is trusted
- pattern: Eval / GroovyShell inside Groovy tools, tests, groovysh, groovyConsole
disposition: "BY-DESIGN: property-disclaimed"
rationale: evaluating code is the product
- pattern: "@Grab / Grape fetching and loading artifacts"
disposition: "OUT-OF-MODEL"
rationale: declared dependency resolution
- pattern: temp-file/dir creation
disposition: KNOWN-NON-FINDING
rationale: NIO owner-only since CVE-2020-17521 (P4)
- pattern: regex (ReDoS), BigInteger/BigDecimal parsing, hash-collision flooding of untrusted map keys
disposition: "OUT-OF-MODEL: downstream-responsibility"
rationale: DoS bounded by developer-chosen input
- pattern: deep recursion / unbounded input in Groovy's OWN data parsers (JsonSlurper, XmlSlurper/XmlParser, groovy-yaml/-toml/-csv)
disposition: VALID-HARDENING
stance_status: maintainer
rationale: >-
robustness of code meant to consume untrusted input; see THREAT_MODEL.md#6. Nesting depth now bounded by
default in all of them — XML mitigated by P2 (secure processing); YAML/TOML/CSV bounded by Jackson
StreamReadConstraints; JsonSlurper enforces a Groovy-level nesting-depth cap (default 1000, matching
StreamReadConstraints; groovy.json.maxNestingDepth / setMaxNestingDepth; GROOVY-12064, 6.0.0). Language
parser excluded (parses trusted source only). No parser-robustness items remain open (see open_items).
- pattern: dynamic proxy / runtime class generation (Proxy, ProxyGenerator)
disposition: KNOWN-NON-FINDING
rationale: core runtime mechanism
- pattern: SecureASTCustomizer bypass demonstrations
disposition: "BY-DESIGN: property-disclaimed"
rationale: it is not a sandbox
stance_status: maintainer; the underlying not-a-boundary fact is documented (its javadoc)
- pattern: Groovy tool/compiler/test output echoing attacker-controlled text, or a dependency emitting agent-targeted text (prompt injection via tool output)
disposition: "OUT-OF-MODEL: downstream-responsibility"
rationale: output is faithful, not sanitized for a downstream sink (shell/browser/SQL/LLM agent); consumer treats it as data; see AGENTS.md data-never-instruction rule
# THREAT_MODEL.md#11b — default-downgrade rules for an automated scanner.
# Downgrade a candidate to out-of-model by default unless its 'unless' also holds.
scanner_calibration:
downgrade_by_default:
- depends_on: evaluating or compiling supplied code/templates
unless: a Groovy component itself feeds untrusted DATA to an evaluator unrequested
- depends_on: deserializing untrusted bytes
unless: a default-config violation of a properties[] entry
- depends_on: equivalent-harm (a new path to authority the actor already holds)
unless: the path crosses a trust boundary the actor did not already hold
- depends_on: malformed-input / DoS against developer-chosen input
unless: the target is one of Groovy's own untrusted-input parsers (VALID-HARDENING)
- depends_on: a malicious dependency, classpath, or groovy.* property
unless: reachability through Groovy's own default-configured code
- depends_on: a PoC the section-7 adversary cannot stage
unless: the capability is within the section-7 closed list
ambiguous: surface as MODEL-GAP for a human; never silently drop
# THREAT_MODEL.md#11c — how serialization findings are dispositioned.
# maintainer-ratified (PMC approval of this document). Baseline: deserializing
# untrusted bytes is the app's risk (out_of_scope) and no per-class change makes
# it safe. Test = capability vs. data.
serialization_policy:
status: maintainer
buckets:
- id: capability-primitive
when: a deserialization path grants a capability beyond reconstructing declared state (method dispatch, class-resolution-by-name, proxy invocation, code exec) AND gating it does not break "restore my data"
disposition: VALID-HARDENING
note: gate it; precedent MethodClosure.ALLOW_RESOLVE (default false) instead of removing Serializable
- id: legitimate-data-serialization
when: round-tripping declared state of POGOs, value types (ranges, tuples, GString), or deliberate closure serialization across a TRUSTED boundary (distributed-compute/caching, both ends owned)
disposition: not-a-finding
note: do not break; no blanket Serializable removal from Closure; if hardened prefer opt-in/transient-by-default for owner/thisObject/delegate (today non-transient) with a rehydration path — an implementation choice, not a scope question
- id: arbitrary-graph-no-primitive
when: DoS/RCE from an arbitrary object graph without naming a specific capability primitive to remove
disposition: "OUT-OF-MODEL: untrusted-deserialization"
note: per-class hardening is whack-a-mole
hinge: bucket capability-primitive vs arbitrary-graph-no-primitive — does the report name a primitive to gate, or just exploit that graphs deserialize? only the former carries a hardening obligation
out_of_scope: # THREAT_MODEL.md#3
- executing — or merely compiling/parsing/statically analyzing — supplied source, scripts, or templates (compile-time metaprogramming/AST transforms run during groovyc, JSR-223 compile, IDE indexing)
- deserializing untrusted data
- absence of a sandbox
- SecureASTCustomizer / AST-filter bypasses
- "@Grab / Grape dependency resolution"
- trusted-input surfaces (classpath, AST transforms, groovy.* properties, ~/.groovy)
- vulnerabilities in the JDK or third-party dependencies (assessed for reachability — a CVE in a part of a dependency Groovy does not use may not affect Groovy)
- embedding-application misuse (hand-built SQL/command strings)
- equivalent-harm (a new path to authority the actor already holds)
- prompt injection aimed at AI agents via dependency/build/test output (supply-chain + agent-toolchain; mitigated by dependency pinning + AGENTS.md data-never-instruction rule)
# THREAT_MODEL.md#3 preamble — out_of_scope is the DEFAULT disposition, not a verdict.
out_of_scope_note: >-
Each report is still assessed for surprising behavior that would catch out a
developer following Groovy's documented secure practices, and can be promoted
to VALID (rarely a CVE), VALID-HARDENING, or MODEL-GAP. When in doubt, report.
misuse_patterns: # THREAT_MODEL.md#11
- untrusted input to Eval/GroovyShell.evaluate/GroovyClassLoader.parseClass/GroovyScriptEngine/JSR-223
- rendering an attacker-controlled template (SimpleTemplateEngine/GStringTemplateEngine/StreamingTemplateEngine/MarkupTemplateEngine/XmlTemplateEngine)
- ObjectInputStream.readObject on untrusted bytes containing Groovy types (crafted graph -> resource exhaustion or historical gadget-chain RCE)
- hand-concatenating untrusted values into a SQL String instead of GString/bind forms
- building a command line from untrusted input and calling .execute()
- option/argument injection — an untrusted value passed as its own argv element (even via the safer String[]/List form) read by the target program as a flag; validate or use a "--" end-of-options separator
- relying on SecureASTCustomizer or @ThreadInterrupt as a sandbox
- enabling @Grab resolution while running untrusted scripts
- parsing untrusted XML after setting allowDocTypeDeclaration = true
# No open items. The former parser-DoS-bounds item was resolved by GROOVY-12064
# (6.0.0): JsonSlurper's nesting-depth cap, captured in properties[P3].scope_note,
# known_non_findings (deep-recursion row), and THREAT_MODEL.md#6.
open_items: []
contacts:
primary: paulk@apache.org
backup: blackdrag@apache.org
report_to:
- security@apache.org
- private@groovy.apache.org