gcp-appengine-versions-create
match (effective permission)
{
"action": "appengine.versions.create",
"resource_type": "google.appengine.Application"
}
emit
| source type | Identity |
|---|---|
| source | <principal> |
| target | <App Engine application> |
| permissions | appengine.versions.create |
| conditions | iam_permission |
| state logic | ACTIVE if source deploy also has Cloud Build + GCS staging perms OR the deploy is a prebuilt container/image (no build needed); else CONDITIONAL(iam_permission) pending build/storage perms. |
Narrative
{principal.name} can deploy a new App Engine version (appengine.versions.create), injecting code that runs as the runtime service account.
Raw rule rules/derived/gcp/appengine.yaml
id: gcp-appengine-versions-create
emits: CanModifyCode
match_effective_permission:
action: appengine.versions.create
resource_type: google.appengine.Application
optional_permission:
- cloudbuild.builds.create
- storage.objects.create
modeling_note: 'Node-type convention for App Engine: the deploy target (application/version) is modeled
as ApplicationPlatform and treated as Compute-like for CanModifyCode purposes; schema/edges.yaml CanModifyCode.target
does not yet list ApplicationPlatform. ExecutesAs is anchored on the Instance (Compute) node. This is
the single consistent convention applied across ExecutesAs, CanModifyCode, CanModifyConfiguration, and
ExposedToInternet in this file (ExposedToInternet uses the PublicEndpoint node, which IS schema-valid).
'
emit:
source_type: Identity
source: <principal>
target: <App Engine application>
permissions:
- appengine.versions.create
conditions:
- iam_permission
state_logic: 'ACTIVE if source deploy also has Cloud Build + GCS staging perms OR the deploy is a prebuilt
container/image (no build needed); else CONDITIONAL(iam_permission) pending build/storage perms.
'
escalation: Deploy a version containing attacker code (reverse shell / token exfil) that runs as the
runtime SA.
false_positive_note: 'Full ''gcloud app deploy'' of source needs roles/cloudbuild.builds.editor + roles/storage.objectAdmin
to build and stage. Without them a source deploy may fail -> emit CONDITIONAL. Container/traffic-split
paths avoid this.
'
narrative: '{principal.name} can deploy a new App Engine version (appengine.versions.create), injecting
code that runs as the runtime service account.'