jamesfredley commented on code in PR #15664: URL: https://github.com/apache/grails-core/pull/15664#discussion_r3274474702
########## THREAT_MODEL.md: ########## @@ -0,0 +1,501 @@ +<!-- +SPDX-License-Identifier: Apache-2.0 + +Licensed 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 + + https://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. +--> + +# Threat Model - Apache Grails + +## §1 Header + +- **Project**: Apache Grails (`apache/grails-core`) +- **Version binding**: 8.0.x branch. A report against version *N* is triaged against this document as it stood at *N*, not at HEAD. +- **Date**: 2026-01 +- **Author**: Apache Grails PMC and contributors (initial draft). +- **Status**: **DRAFT** - not yet ratified by maintainers. Open questions in §14 must be resolved before this document is binding. +- **Reporting cross-reference**: findings that may violate a property claimed in §8 should be reported privately per [`SECURITY.md`](./SECURITY.md) (which routes to the [ASF Security Team](https://www.apache.org/security/)). Findings that fall under §3 (out of scope), §9 (disclaimed properties), or §11a (known non-findings) will be closed publicly citing the relevant section of this document. +- **Provenance legend**: every non-trivial claim is tagged. + - *(documented)* - stated in the project's own docs ([`grails-doc/src/en/guide/security/`](./grails-doc/src/en/guide/security/), [`SECURITY.md`](./SECURITY.md), [`README.md`](./README.md), [`AGENTS.md`](./AGENTS.md), or the public Grails Guide). + - *(maintainer)* - stated by a maintainer in response to a question from this drafting process. + - *(inferred)* - reasoned from code structure, absence of a feature, or general domain knowledge. Each must have a matching entry in §14. +- **Draft confidence**: ~46 *(documented)* / 0 *(maintainer)* / ~58 *(inferred)*. This model is a draft-first artifact per the rubric §3.2 - the *(inferred)* count is expected to drop sharply once §14 is worked through with the PMC. + +**Project description**: Apache Grails is an opinionated, full-stack web application framework for the JVM. It composes Apache Groovy, Spring Framework, Spring Boot, GORM (Hibernate / MongoDB / Neo4j data mapping), and GSP/JSON view rendering, and ships a CLI (`grails-shell-cli`, `grails-forge-cli`) plus an HTTP application generator (`grails-forge`). Grails is **embedded into a user-authored web application**; it is not deployed as a standalone network service by the project. The unit of trust modeled here is "an application using the Grails framework," not "a Grails server." *(documented: [README.md](./README.md))* + +--- + +## §2 Scope and intended use + +**Primary intended use**: building server-side web applications in Groovy/Java on the JVM, deployed as Spring Boot executable JARs or WARs running under an operator the application owner controls. *(documented: [README.md](./README.md))* + +**Secondary intended use**: project scaffolding and code generation via the CLI tools and `grails-forge` HTTP API at <https://start.grails.org>. *(documented: [README.md](./README.md))* + +**Caller roles** (the model distinguishes three; this is not a network-service split since the framework is in-process within the user's app): + +| Role | Trust level | Description | +|---|---|---| +| **End user (HTTP client)** | **Untrusted** | Sends HTTP requests to a deployed Grails application. Source of all attacker-controllable input considered in this model. *(inferred)* | +| **Application developer / operator** | **Trusted** | Writes controllers, services, domain classes, URL mappings, GSP templates; configures `application.yml` / `application.groovy`; runs the CLI; chooses plugins. *(inferred)* | +| **Plugin / profile author** | **Trusted-by-association** | Author of a third-party Grails plugin or `grails-forge` profile. Code from a plugin runs with full application privileges. The framework does not isolate plugin code. *(inferred)* | + +### Component-family table + +The framework is large; not every module has the same threat profile. The model carves the framework into the following families: + +| Family | Representative entry point(s) | Touches outside process? | In or out of model | +|---|---|---|---| +| HTTP request ingress | [`GrailsDispatcherServlet`](./grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsDispatcherServlet.groovy), [`UrlMappingsHandlerMapping`](./grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/mvc/UrlMappingsHandlerMapping.groovy) | Yes - network (via Spring Boot embedded container) | **In** | +| Interceptors / controllers | [`GrailsInterceptorHandlerInterceptorAdapter`](./grails-interceptors/src/main/groovy/org/grails/plugins/web/interceptors/GrailsInterceptorHandlerInterceptorAdapter.groovy), `grails.artefact.Controller` trait | Yes - via Spring MVC | **In** | +| Data binding | [`GrailsWebDataBinder`](./grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy), `SimpleDataBinder`, [`DefaultDataBindingSourceRegistry`](./grails-web-databinding/src/main/groovy/org/grails/web/databinding/bindingsource/DefaultDataBindingSourceRegistry.groovy) | Indirectly (consumes request body) | **In** | +| View rendering (GSP, JSON, Markup) | [`GroovyPageCompiler`](./grails-gsp/core/src/main/groovy/org/grails/gsp/compiler/GroovyPageCompiler.groovy), [`ResolvableGroovyTemplateEngine`](./grails-views-core/src/main/groovy/grails/views/ResolvableGroovyTemplateEngine.groovy), [`SmartViewResolver`](./grails-views-core/src/main/groovy/grails/views/mvc/SmartViewResolver.groovy) | Filesystem (template files) | **In** | +| Codecs / output encoding | [`grails-encoder`](./grails-encoder/), [`HTMLCodec`](./grails-encoder/src/main/groovy/org/grails/encoder/CodecFactory.java) and siblings | No | **In** | +| GORM mapping / validation integration | [`GormEntityTransformation`](./grails-datamapping-core/src/main/groovy/org/grails/compiler/gorm/GormEntityTransformation.groovy), [`PersistentEntityValidator`](./grails-datamapping-validation/src/main/groovy/grails/gorm/validation/PersistentEntityValidator.groovy), [`HibernateMappingBuilder`](./grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateMappingBuilder.groovy) | Yes - JDBC / DB driver | **In** | +| Configuration loading | [`ExternalConfigRunListener`](./grails-core/src/main/groovy/grails/config/external/ExternalConfigRunListener.groovy), [`GroovyConfigPropertySourceLoader`](./grails-core/src/main/groovy/org/grails/core/cfg/GroovyConfigPropertySourceLoader.groovy), [`GrailsApplicationPostProcessor`](./grails-core/src/main/groovy/grails/boot/config/GrailsApplicationPostProcessor.groovy) | Yes - filesystem, env, system properties | **In** | +| Plugin / artefact discovery | [`PluginManagerLoader`](./grails-core/src/main/groovy/grails/plugins/PluginManagerLoader.groovy), [`ClassPathScanner`](./grails-core/src/main/groovy/grails/boot/config/tools/ClassPathScanner.groovy), [`GrailsFactoriesLoader`](./grails-core/src/main/groovy/org/grails/core/io/support/GrailsFactoriesLoader.groovy) | Filesystem (classpath JARs) | **In** | +| Compile-time AST transforms | [`GlobalGrailsClassInjectorTransformation`](./grails-core/src/main/groovy/org/grails/compiler/injection/GlobalGrailsClassInjectorTransformation.groovy), [`ResourceTransform`](./grails-rest-transforms/src/main/groovy/org/grails/plugins/web/rest/transform/ResourceTransform.groovy), [`ViewsTransform`](./grails-views-core/src/main/groovy/grails/views/compiler/ViewsTransform.groovy) | No (build only) | **In** (developer-trusted inputs only - see §6) | +| CLI / shell (`grails-shell-cli`) | [`GrailsCli`](./grails-shell-cli/src/main/groovy/org/grails/cli/GrailsCli.groovy), [`MavenProfileRepository`](./grails-shell-cli/src/main/groovy/org/grails/cli/profile/repository/MavenProfileRepository.groovy) | Yes - network (Maven), filesystem | **In** (with caveats - see §3 and §7) | +| Interactive consoles (`grails-console`) | [`GrailsShell`](./grails-console/src/main/groovy/grails/ui/shell/GrailsShell.groovy), `GroovyConsoleApplicationContext` | Local I/O | **Out** - developer-only tool, see §3 | +| `grails-forge` HTTP API (start.grails.org) | [`ZipCreateController`](./grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/zip/ZipCreateController.java), [`GitHubCreateController`](./grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/github/GitHubCreateController.java) | Yes - network | **Out** - separate Micronaut-based service deployed independently from any application built with Grails; modeled separately by the operators of start.grails.org. *(inferred)* | +| Generated application scaffolding | `grails-profiles/`, files emitted by `create-app` | n/a | **Out** - separately authored; the framework's contract does not extend to scaffolded code once it is in the user's repository. See §3. | +| `grails-test-examples/`, demo apps | n/a | n/a | **Out** - test/demo code, not shipped. See §3. | + +--- + +## §3 Out of scope (explicit non-goals) + +The framework **does not** attempt to defend against, and **does not** model, the following. Triagers may close findings citing this section. + +- **Application-level authentication and authorization.** The framework ships no built-in user store, login flow, or session-based auth. The user is expected to integrate with the Spring Security plugin, Apache Shiro, or an equivalent. The `grails-shell-cli` `SpringSecurityCompilerAutoConfiguration` is a compile-time hook for the plugin, not an auth implementation. *(documented: [grails-doc/src/en/guide/security/authentication.adoc](./grails-doc/src/en/guide/security/authentication.adoc), [grails-doc/src/en/guide/security/securityPlugins.adoc](./grails-doc/src/en/guide/security/securityPlugins.adoc))* +- **Transport security (TLS).** Provided by the Spring Boot embedded container (Tomcat / Jetty / Undertow / Netty) or by a reverse proxy in front of the application. Out of layer. *(inferred)* +- **Database engine, JDBC driver, and JVM vulnerabilities.** The framework is a consumer; bugs in these layers are upstream. *(inferred)* +- **Spring Framework, Spring Boot, Hibernate, GORM datastore implementations.** Triaged in their own projects; the framework re-exposes their public APIs but does not own their threat models. *(inferred)* +- **Third-party Grails plugins** (anything not in this repository). Plugins run with full application privileges; their threat models are the responsibility of their authors. *(inferred)* +- **Generated application scaffolding output** (files emitted by `create-app` or `grails-forge`). Once written to the user's filesystem, the output is the user's code. The generator does not promise the generated code remains free of advisories as Grails evolves. *(inferred)* +- **`grails-test-examples/`, demo and reproducer modules**, and any `examples/` or scaffold output checked into the repository. Not shipped in framework distributions; threat-model separately if used as a starting point. *(inferred)* +- **Interactive consoles (`grails-console` module: `GrailsShell`, `GroovyConsole`).** These provide arbitrary Groovy code execution with full application-context access **by design**. They are developer tools. Reachability of these consoles from a deployed application is a deployment-configuration finding against the operator, not a framework vulnerability. *(inferred)* +- **`grails-forge` HTTP service at start.grails.org**, including the `ZipCreateController` / `GitHubCreateController` endpoints. The Micronaut-based service is deployed independently, has its own operator, and is modeled separately. The Grails framework neither runs this service inside user applications nor inherits its risk. *(inferred)* +- **Build-time supply chain** (Gradle plugin portal, Maven Central, signing, reproducible builds, GitHub Action pinning). Important, but not threat-model content per the rubric §1. The framework does have a CodeQL workflow ([`.github/workflows/codeql.yml`](./.github/workflows/codeql.yml)) and a CycloneDX SBOM tooling path, but neither makes a security claim that belongs in this document. *(documented: [`.github/workflows/codeql.yml`](./.github/workflows/codeql.yml))* +- **Side-channel attacks** (timing, cache, power, micro-architectural). No constant-time guarantees are made anywhere in the framework. *(inferred)* + +--- + +## §4 Trust boundaries and data flow + +The principal trust boundary modeled here is the **HTTP request boundary**: data crossing from an end user (untrusted) into a Grails application's controller layer. A secondary boundary is the **filesystem / environment boundary** at application startup, where configuration is loaded. + +### Primary data flow (HTTP request → response) + +``` +[Untrusted end user] + | + v (HTTP bytes; transport security is the operator's responsibility - §3) +[Embedded servlet container] <-- boundary: not framework + | + v +[GrailsDispatcherServlet] <-- HTTP request enters framework + | + v +[UrlMappingsHandlerMapping] (regex/wildcard match against developer-authored UrlMappings DSL) + | + v +[GrailsInterceptorHandlerInterceptorAdapter] (developer-authored interceptors) + | + v +[Controller action] (developer-authored; trusted-code, untrusted-data) + | | + | +--> [GrailsWebDataBinder] (request params/body -> domain object) <-- HIGHEST-RISK SURFACE + | + v +[Domain services, GORM] (developer-authored business logic; GORM escapes parameterized queries) + | + v +[View rendering: GSP / JSON / Markup] (output encoding via codecs - §8) + | + v +[Embedded servlet container] + | + v +[End user] +``` + +The trust transition occurs at `GrailsDispatcherServlet`. Within the framework, **request parameters, headers, cookies, and request bodies are treated as attacker-controlled**. The output codec layer is the matching transition on the response side - data leaving the framework into a view template is escaped per the view's content type. *(documented: [grails-doc/src/en/guide/security/xssPrevention.adoc](./grails-doc/src/en/guide/security/xssPrevention.adoc), [grails-doc/src/en/guide/security/codecs.adoc](./grails-doc/src/en/guide/security/codecs.adoc))* + +### Reachability preconditions per component family + +A triager applies these tests before deciding a finding is in-model: + +| Component family | Reachability precondition for a finding to be in-model | +|---|---| +| HTTP request ingress | Reachable from an HTTP request with no developer-authored guard preceding it. *(inferred)* | +| Data binding | Reachable via a controller action that calls `bindData()`, uses command objects, or accepts a domain class parameter, AND the bound fields are not declared `bindable: false`. *(inferred)* | +| View rendering | Reachable when developer-supplied model data flows into a GSP/JSON view AND output encoding is either disabled or bypassed via `${raw()}`. *(inferred)* | +| Codecs | Reachable from any call to an encoder API; the codec subsystem itself is a control, not a sink. *(inferred)* | +| GORM mapping integration | Reachable from a query path that accepts attacker-controlled values. HQL string concatenation, dynamic finder property names sourced from `params`, and `where`-DSL closures dynamically built from input are the canonical risk paths. *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc) "SQL injection")* | +| Configuration loading | Reachable only if the attacker controls `grails.config.locations`, `GRAILS_CONFIG_LOCATIONS`, or the application classpath. **All three require local privilege at or above the application process** - therefore out of model in the default web-app scenario. *(inferred)* | +| Plugin / artefact discovery | Reachable only via a malicious JAR on the classpath. Same precondition as above - **classpath compromise is out of model**. *(inferred)* | +| Compile-time AST transforms | Reachable only at build time, with developer-controlled source files. Not reachable from an end user. *(inferred)* | +| CLI / shell (`grails-shell-cli`) | Reachable by the developer running the CLI on their local machine. End users do not invoke the CLI; if they can, the host is already compromised. *(inferred)* | + +The reachability column for **configuration loading** and **plugin discovery** is the load-bearing claim of this section: the framework's most powerful capabilities (Groovy DSL evaluation, classloader injection) are guarded by "code on the classpath is trusted." A report against these surfaces that does not first demonstrate untrusted control of the classpath or `grails.config.locations` is `OUT-OF-MODEL: trusted-input`. See §13. + +--- + +## §5 Assumptions about the environment + +**Runtime**: +- JDK 21 or higher. *(documented: [AGENTS.md](./AGENTS.md))* +- Apache Groovy 4.0.x. *(documented: [AGENTS.md](./AGENTS.md))* +- Spring Boot 4.0.x / Spring Framework 7.0.x. *(documented: [AGENTS.md](./AGENTS.md))* +- Jakarta EE 10 servlet API (`jakarta.*`, not `javax.*`). *(documented: [AGENTS.md](./AGENTS.md))* + +**Operator-controlled environment**: +- The application is deployed by a trusted operator on hardware and OS the operator controls. *(inferred)* +- The embedded servlet container is fronted by, or itself provides, transport security (TLS). *(inferred)* +- The application classpath contains only artifacts the operator/developer chose - no untrusted JAR is loaded at runtime. *(inferred)* +- Environment variables and system properties consulted at startup (`GRAILS_ENV`, `grails.env`, `grails.config.locations`, and the standard Spring Boot set) reflect operator intent. *(inferred)* + +**Concurrency**: +- The framework assumes a thread-per-request servlet model. Reactive (`WebFlux`) deployment is supported by Spring Boot but is not the modeled default. *(inferred)* +- `GrailsWebRequest.lookup()` provides thread-local access to the active request; the framework assumes one logical request per thread. *(documented: [AGENTS.md](./AGENTS.md))* + +### What the framework does NOT do to its host + +These are **negative claims** about the framework's behavior. By the rubric they are the lowest-confidence claims in the model and the highest-priority targets for maintainer confirmation (see §14). + +- The framework does not bind sockets directly. All network listening is delegated to the Spring Boot embedded container, configured by the operator. *(inferred)* +- The framework does not spawn child processes from the runtime layer. The CLI (`grails-shell-cli`) does spawn build tools (Gradle) - but only when invoked by a developer at the terminal. *(inferred)* +- The framework does not install JVM signal handlers. *(inferred)* +- The framework does not read environment variables beyond `GRAILS_ENV` / `grails.env` / `grails.config.locations` and the Spring Boot standard set. *(inferred)* +- The framework does not mutate global JVM state (default `Locale`, default `TimeZone`, system properties) at runtime. It does mutate `Locale` and `TimeZone` for the duration of a request via `GrailsWebRequest` for i18n - this is request-scoped, not process-wide. *(inferred)* +- The framework does not write to stdout or stderr at runtime beyond SLF4J-routed logging. *(inferred)* +- The framework does not load classes from network locations at runtime. (The CLI does, via `MavenProfileRepository`, but that is the developer surface - see §6 and §7.) *(inferred)* + +--- + +## §5a Build-time and configuration variants + +The framework exposes a small number of configuration knobs whose value affects which security properties hold. Defaults are listed; "Maintainer stance" is a §14 target. + +| Knob | Default | Effect on the model | Maintainer stance | +|---|---|---|---| +| `grails.databinding.autoGrowCollectionLimit` | 256 *(documented: framework default)* | Caps automatic collection growth during data binding - hard limit on memory amplification from an attacker submitting deeply indexed parameters (`list[1000000]=x`). Raising removes the cap. | **§14 wave 2** - is the documented default the supported production posture, or is the operator expected to lower it? | +| `grails.databinding.dateFormats` / `dateParsingLenient` | RFC-3339 + locale defaults; lenient parsing on | Affects how strict date binding is. Loose parsing has historically been a source of validation-bypass findings in other frameworks. | **§14 wave 2** | +| `grails.views.default.codec` and codec defaults (`grails.views.gsp.codecs.expression`, `scriptlet`, `taglib`, `staticparts`) | `html` for expression / scriptlet contexts (XSS protection on by default) | Setting any of these to `none` **disables automatic output encoding** for that context - immediate `OUT-OF-MODEL: non-default-build` for XSS reports under non-default settings. *(documented: [xssPrevention.adoc](./grails-doc/src/en/guide/security/xssPrevention.adoc), [codecs.adoc](./grails-doc/src/en/guide/security/codecs.adoc))* | **§14 wave 1** - confirm the `html` default is the supported production posture. | +| `grails.controllers.upload.maxFileSize` / `maxRequestSize` | Operator-set; framework does not impose a default beyond Spring Boot's `MultipartProperties` (1 MB / 10 MB respectively) | Multipart upload size cap. Operators who raise these without separate rate-limiting expose themselves to DoS via large multipart bodies. | **§14 wave 2** | +| `grails.allowedMethods` (per-controller) | None (developer opt-in) | Restricts HTTP methods accepted by each action. Absence is **not** a finding; the model treats per-action method gating as a developer responsibility. *(inferred)* | **§14 wave 1** | +| `grails.config.locations` (env var, system property, or config) | Empty | Adds external config file paths. **A non-empty value sourced from an untrusted location is a `BY-DESIGN: property-disclaimed` triage outcome** - see §9. | **§14 wave 1** - confirm this disposition. | +| `GRAILS_ENV` / `grails.env` | `development` from CLI, `production` for assembled bootJars | Selects the active environment block in `application.yml` / `application.groovy`. Operators who deploy with `GRAILS_ENV=development` inherit the looser dev defaults (e.g., stack traces in responses). | **§14 wave 1** - is deploying with `development` a `non-default-build` posture? | +| `grails.serverURL` | Operator-set; required for absolute link generation outside a request | Used by `createLink` and similar tag helpers when no request scope exists. Misconfiguration is a phishing/open-redirect adjacent issue but is a deployment-config concern, not a framework bug. *(inferred)* | **§14 wave 2** | + +There is **no compile-time `-D` define or build flag** that voids a §8 property; the model is invariant under build configuration. *(inferred - §14 wave 3)* + +--- + +## §6 Assumptions about inputs + +The framework's public input boundary is the HTTP request. Per-parameter trust is summarized below. The table is intentionally framework-level; per-application controllers, services, and domain classes are out of scope (developer-authored). + +### Per-parameter trust table + +| Entry point / surface | Parameter | Attacker-controllable? | Caller (developer) must enforce | +|---|---|---|---| +| `Controller.params` | All values | **Yes** - direct request parameter map | Type coercion correctness; never concatenate into HQL/SQL/JPQL/Groovy strings; never use as redirect target without an allow-list. *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc) "XSS", "HTML/URL injection")* | +| `Controller.request.headers` | All values | **Yes** - including `X-Forwarded-*`, `Host`, `User-Agent`, `Referer`, custom auth tokens | Treat presence as evidence of nothing; auth headers must be verified against the configured auth subsystem (Spring Security or equivalent). *(inferred)* | +| `Controller.request.cookies` | All values | **Yes** | Treat as attacker-supplied; if used for auth, integrity-protect via Spring Security or signed cookies. *(inferred)* | +| `Controller.request.JSON` / `XML` | Full body | **Yes** | Parser inputs are bounded by `maxRequestSize`; nested-depth limits are the parser's responsibility (Jackson, JAXP). *(inferred)* | +| `bindData(target, source)` | `source` (any `Map` or request) | **Yes** for the source; **No** for the target type (developer-controlled) | Use `bindable`/`include`/`exclude` to whitelist fields. The framework will bind every settable property of `target` from matching keys in `source` unless told otherwise. *(documented: [GORM data binding guide](https://grails.apache.org/docs/latest/guide/single.html#dataBinding))* | +| Command-object binding (auto-bound controller action parameter) | Field values | **Yes** | Annotate command-object fields with `bindable=false` for fields that must not be set from the request. *(inferred)* | +| Domain-class binding (`new Book(params)`, `book.properties = params`) | Field values | **Yes** | **Mass-assignment risk.** Use command objects or explicit allow-lists rather than binding the request to a domain class. *(inferred - canonical OWASP class)* | +| `MultipartFile` upload | `bytes`, `originalFilename`, `contentType` | **Yes** | Never use `originalFilename` as a filesystem path without sanitization; verify `contentType` server-side against the actual byte content. *(inferred)* | +| URL mapping path variables | Pattern captures (`/$id`, `/$controller/$action/$id`) | **Yes** | Validate type and range; `params.id` is an arbitrary string until validated. *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc) "Guessable IDs")* | +| GSP expression `${...}` (default mode) | Model value | **Yes** | Default codec encodes for HTML; explicit `${raw(x)}` opt-outs are a developer assertion that `x` is trusted. *(documented: [xssPrevention.adoc](./grails-doc/src/en/guide/security/xssPrevention.adoc))* | +| `grails.config.locations` (env / system property) | File path | **No - trusted operator input** | Operators must not source this value from end-user input. See §9 false-friend on Groovy config evaluation. *(inferred)* | +| `application.groovy` / `application.yml` contents | All keys | **No - trusted operator input** | `application.groovy` is evaluated as Groovy; treat it like source code, not configuration. *(inferred)* | +| Domain `mapping {}` / `constraints {}` closure | Closure body | **No - trusted developer input** | Evaluated at startup via `HibernateMappingBuilder.evaluate()` and `ConstraintsEvaluator`. Developer-authored code. *(inferred)* | +| AST transform inputs (`@Resource`, `@Validateable`, etc.) | Annotated source | **No - trusted developer input** | Run at compile time on developer source. *(inferred)* | +| `MavenProfileRepository` (CLI) | Profile coordinates | **Trusted developer input by default; trusted-third-party if a custom repo is configured** | The CLI downloads profile JARs and executes their `.groovy` command scripts (`GroovyScriptCommand`). Treat profile sources with the same trust as any other build-time dependency. See §11. *(inferred)* | + +### Size, shape, rate assumptions + +- Request body size: bounded by the embedded container / Spring Boot multipart settings, not by the framework itself. *(inferred)* +- Number of bound collection entries during data binding: bounded by `grails.databinding.autoGrowCollectionLimit` (default 256). *(documented: framework default)* +- URL mapping pattern complexity: developer-authored; the framework does not enforce a complexity ceiling on regex / wildcard patterns. ReDoS risk is therefore left to the developer. See §11. *(inferred)* +- Request rate: no built-in rate limiting; operators must add it at the proxy or via Spring Security / bucket4j. *(inferred)* + +--- + +## §7 Adversary model + +### In-scope adversary: the HTTP end user + +The principal adversary is a remote, unauthenticated HTTP client. Capabilities: + +- Crafts arbitrary HTTP requests against any URL pattern the application exposes. +- Sends arbitrary headers, cookies, query parameters, form bodies, JSON bodies, XML bodies, and multipart uploads up to operator-configured size limits. +- May replay or modify requests sourced from authenticated sessions in a CSRF context. *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc) "Cross-site request forgery")* +- May enumerate URL spaces, including guessable `id` values. *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc) "Guessable IDs")* + +What this adversary does **not** have: + +- Read or write access to the application filesystem, classpath, environment variables, or system properties. +- Ability to inject classes into the classpath or modify `application.groovy` / `application.yml`. +- Co-location on the same JVM as the application. +- Side-channel observation (timing, cache, power). +- Ability to compromise the JDK, Spring, Hibernate, or any third-party plugin. + +**Adversary goal**: cause the framework to violate one of the properties in §8 - typically by injecting code (XSS, HQL injection, Groovy injection), exfiltrating data the developer did not authorize disclosure of, or exhausting resources (DoS). + +### Documented adversary-model statement + +> "You must assume that every unprotected URL is publicly accessible one way or another." *(documented: [securingAgainstAttacks.adoc](./grails-doc/src/en/guide/security/securingAgainstAttacks.adoc))* + +This is the framework's stated position: URLs without developer-applied protection are reachable by the in-scope adversary. + +### Out-of-scope adversaries + +- **Local attacker with shell access on the application host.** Such an attacker can modify config, JARs, env vars, and the JVM itself. The framework cannot defend against them and does not try. *(inferred)* +- **Compromised plugin / JAR on the application classpath.** Plugins run with full privileges by design. *(inferred)* +- **Compromised build environment.** Out of model per §3. +- **Co-tenant attacker on the same JVM (e.g., another application in a shared servlet container).** The framework does not assume process-level isolation between deployed applications. The model assumes one application per JVM. *(inferred)* +- **Attacker who controls a Grails plugin or `grails-forge` profile downloaded by a developer running the CLI.** This is the supply-chain surface around `MavenProfileRepository`; see §11. The framework does not verify profile JAR signatures beyond what Maven Resolver provides. *(inferred)* Review Comment: changing this to 'grails' instead of 'grails-forge' -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
