Module 5: Securing The Ai Workload

7. STRIDE, revisited: the threats specific to an AI workload

Description

THREAT-MODEL.md (cloud-security-and-guardrails-guide, Module 1, lesson 7) has seven rows — TM-01 through TM-07 —, each mapped to a STRIDE category, with its evidence and its mitigation module. None of the seven mentions a language model, because when that document was written, Andes Cargo didn't yet have any generative AI workload. This lesson doesn't rewrite the document or renumber anything existing: it adds two new rows, TM-08 and TM-09, each with the same verifiable-evidence discipline the seven originals already established.

Connection to the module

This lesson's two new rows aren't unanchored hypotheses — each cites the real control that already mitigates it, built in an earlier module of this guide: Bedrock's managed guardrail (Module 4) for TM-08, and the custom PII scrubber (Module 4, lesson 5) for TM-09. This lesson documents what already exists, with the same format cloud-security-and-guardrails-guide, Module 1, lesson 7 already established — it builds no new control.


Why STRIDE needs two new rows, not a complete rewrite

cloud-security-and-guardrails-guide, Module 1, lesson 3, already explained STRIDE's six categories: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. No category is exclusive to "traditional" infrastructure versus AI infrastructure — STRIDE is a general framework, applicable to any system. What changes, when adding a generative AI workload, isn't the framework: it's that two of the six categories acquire a new manifestation, specific to invoking a non-deterministic model, which the seven original rows — written before Bedrock existed in this project — could never cover.

   THE SEVEN ORIGINAL ROWS                        THIS MODULE'S TWO NEW ROWS

   TM-01  Spoofing            (M2)                TM-08  Tampering
   TM-02  Tampering           (M6)                       of the *prompt* -- specific to invoking
   TM-03  Repudiation         (M7)                       a model, never existed before this
   TM-04  Information disc.   (M4)                       guide
   TM-05  Information disc.   (M3)
   TM-06  Denial of service   (M4)                TM-09  Information disclosure
   TM-07  Elevation priv.     (M2)                       of PII in a free-text manifest --
                                                          different from TM-04/TM-05 (both about
                                                          infrastructure, not content)

TM-02 already covers Tampering of the deployment artifact (an unsigned function.zip, resolved by cosign in that guide's Module 6) — but it never contemplated a model request's content being manipulated to make the system act outside its purpose. TM-04 and TM-05 already cover infrastructure Information disclosure (a bucket without a public-access block, plaintext credentials) — but neither contemplated a free-text manifest, the same type of data that motivates this entire guide (Module 1, lesson 3), carrying incidental PII a language model processes and, potentially, echoes back.


TM-08 — Tampering: attempted prompt manipulation

Category: Tampering. Finding: extract-shipment-manifest-fields receives uncontrolled free text — an email body, a note copied from a logistics partner's system (Module 1, lesson 3) — as direct input to a language model. With no control, that text could contain instructions designed to make the model ignore its real task (extracting manifest fields) and instead follow instructions injected by whoever wrote the manifest — a prompt-injection attempt, the same threat class this guide's Module 4, lesson 7 already built as a representative example.

Evidence: bedrock.tf (Module 4, lesson 3) declares content_policy_config with a filters_config of type = "PROMPT_ATTACK", input_strength = "HIGH" — the very existence of that policy, with that specific configuration, is documented evidence that this risk was identified and a mitigation was designed for it, before this module got around to formally documenting it in THREAT-MODEL.md.

Impact: Without this control, a malicious manifest could make the model reveal system information (a system prompt, if a more elaborate one existed), or produce output that doesn't correspond at all to the expected field extraction — potentially writing incorrect or malicious data into Shipments if post_invoke_checks.py didn't stop it first.

Mitigated in: Module 4, lesson 3 (content_policy_config, PROMPT_ATTACK filter, HCL declared and really plan-ned) — with the exact honesty this guide's Module 4, lesson 7 already established: real blocking of a real prompt-injection attempt can only be confirmed by actually invoking the model, something this $0 lab never does. The mitigation is declared as code, verified with terraform validate/plan; its effectiveness against a real attack stays representative, precisely cited against ApplyGuardrail's official schema.


TM-09 — Information disclosure: incidental PII leak in a manifest

Category: Information disclosure. Finding: a real free-text manifest, the type that motivates this guide, frequently contains incidental contact data — an email address, a phone number — naturally embedded in the prose of whoever wrote it, with no malicious intent whatsoever. With no control, that text would travel, as-is, all the way to the Bedrock model, and potentially show up back in the response or in any log the system generates.

Evidence: guardrails/pre_invoke_checks.py (Module 4, lesson 5) exists precisely because this risk was identified as real and concrete, not hypothetical — the script itself detects and redacts emails and phone numbers with regular expressions, run and verified with pytest against a real manifest from Module 1 (shipment AC-4471) that deliberately contains an email and a phone number in its prose.

Impact: A personal contact detail — email, phone — could end up exposed in a model response, an invocation log, or any system that receives the unredacted text, violating the reasonable privacy expectation of whoever wrote the manifest, without that person knowing their personal information was going to travel to a third-party inference service.

Mitigated in: two independent layers, neither a substitute for the other (the same defense-in-depth argument this guide's Module 4, lesson 4 already developed): guardrails/pre_invoke_checks.py (deterministic, custom, always runs, regardless of whether Bedrock exists) and bedrock.tf's sensitive_information_policy_config with EMAIL/PHONE under ANONYMIZE (managed, probabilistic, evaluated by Bedrock itself). Unlike TM-08, this row has a mitigation that can be fully confirmed executed, with no representative part at all: pre_invoke_checks.py ran for real, with pytest, and the result (PII FOUND, EMAIL matches: 1, PHONE matches: 1, redacted text) is literal, not a construction over a documented schema.


Updating THREAT-MODEL.md: the two new rows

  | TM-06 | Denial of service | No control blocks a `plan` that destroys `Shipments` | No preventive policy evaluates `resource_changes[].actions` before `apply` | A bad `apply` -- human error or a misled agent -- removes the system's sole source of shipment state | Module 4 (`no-destroy-shipments.rego`) |
  | TM-07 | Elevation of privilege | `AppServerRole` has the same full-bucket scope as `LambdaManifestProcessorRole` | `AppServerRole-policy` == `LambdaManifestProcessorRole-policy` (same actions, same resource ARNs) | A compromise of the read-only tracking app grants access to raw manifests it never needed to read | Module 2, lesson 7 (least-privilege role tightening) |
+ | TM-08 | Tampering | Free-text manifest content could carry a prompt injection attempt against the extractor's model call | `bedrock.tf`, `content_policy_config.filters_config` (`type = "PROMPT_ATTACK"`, `input_strength = "HIGH"`) -- genai-on-aws-production-guide, Module 4, lesson 3 | An attacker-controlled manifest could redirect the model away from field extraction toward following injected instructions | genai-on-aws-production-guide, Module 4, lesson 3 (declared, `validate`/`plan` real; real blocking representative -- Module 4, lesson 7) |
+ | TM-09 | Information disclosure | A free-text manifest routinely carries a sender's email or phone number embedded in the prose, reaching an inference service outside this account | `guardrails/pre_invoke_checks.py` (EMAIL/PHONE regex, pytest-verified) + `bedrock.tf` `sensitive_information_policy_config` (EMAIL/PHONE, ANONYMIZE) -- genai-on-aws-production-guide, Module 4, lessons 3 and 5 | A shipper's personal contact data could leak into a model response or an invocation log | genai-on-aws-production-guide, Module 4, lessons 3 and 5 (defense in depth, both layers executed) |

Notice a detail worth precisely naming: unlike the seven original rows, whose "Mitigated in" column points to a module in the same guide (cloud-security-and-guardrails-guide), these two rows point to modules in this guide (genai-on-aws-production-guide). This is correct, not a formatting error: THREAT-MODEL.md is a shared document for the andes-cargo-infra/ project, and any guide that adds new infrastructure to that same project — this guide included — has the same responsibility to keep it updated that cloud-security-and-guardrails-guide, Module 1, lesson 7, section 6 ("Document maintenance") already explicitly declared: "reviewed, not rewritten from scratch, whenever a new resource is added to andes-cargo-infra/".


Verifying the updated document

grep -c '^| TM-' THREAT-MODEL.md

What to expect (literal — nine rows, the seven original ones plus this lesson's two; you wrote this document's content yourself, so its shape is deterministic):

9

Common mistakes

Renumbering the seven original rows to "reorganize" the document by guide order, instead of appending at the end (touching what isn't yours to touch). What happens: someone, seeing that the two new rows belong to a different guide, tries to reorder THREAT-MODEL.md by grouping rows by their originating guide, renumbering TM-01 through TM-09 in a different order. How to spot it: if your version of THREAT-MODEL.md has TM-* identifiers that don't match what cloud-security-and-guardrails-guide, Module 1, lesson 7 already fixed for the seven original rows. How to fix it: the TM-01 through TM-07 identifiers are permanent, exactly as that lesson already declared — "from this document forward, any later module of this guide can refer to 'TM-06' without having to re-explain what it is every time". Renumbering would break any existing reference to those identifiers in other project documents. New rows always get appended at the end, with the next available number.

Writing TM-08/TM-09 without citing the exact file or resource that mitigates them, repeating the mistake cloud-security-and-guardrails-guide, Module 1, lesson 7 already warned about (vagueness in the evidence). What happens: someone writes a generic finding like "the model could be manipulated" without naming the file, the line, or the specific HCL argument that mitigates it. How to spot it: if your TM-08/TM-09 row doesn't include a verifiable filename (bedrock.tf, pre_invoke_checks.py) someone could open and confirm. How to fix it: every row in this lesson cites the exact file and argument (content_policy_config.filters_config, sensitive_information_policy_config) — the same specificity that separates a real threat model from a list of unanchored concerns, the same discipline that document established from its first version.

Confusing TM-08 with a duplicate of TM-02 (Tampering, the deployment artifact) because both share the same STRIDE category (grouping by category instead of by mechanism). What happens: someone, seeing TM-02 and TM-08 share the Tampering category, assumes they're the same risk documented twice. How to spot it: if your explanation of why both rows exist doesn't distinguish what gets manipulated in each case. How to fix it: TM-02 is tampering of the binary artifact — the deployed .zip, resolved with a cryptographic signature (cosign) —; TM-08 is tampering of a model request's content — a manifest's text, resolved with a content filter (content_policy_config). STRIDE groups threats by attack mechanism (manipulating something that should stay intact), not by specific surface — two distinct rows, in the same category, covering completely different surfaces, is exactly how STRIDE is designed to be used.


Exercises

Exercise 1 — Without looking at this lesson, write from memory the two STRIDE categories TM-08 and TM-09 use. Then check your answer and explain why neither is Denial of service nor Elevation of privilege, the two categories that at first glance might also seem relevant to an AI workload.

See solution

TM-08 is Tampering, TM-09 is Information disclosure. Denial of service doesn't apply because no risk in this lesson describes a way of taking the system out of service — a prompt-injection attempt doesn't take down extract-shipment-manifest-fields, at worst it produces an incorrect response post_invoke_checks.py (Module 4, lesson 6) would reject. Elevation of privilege doesn't apply either because neither risk describes someone gaining permissions they shouldn't have — that's already covered by TM-07, and this module's lessons 2/3, with bedrock-least-privilege.rego, are what watches that specific category for the AI infrastructure.

Exercise 2 — Explain why TM-09 has a fully executed mitigation (with no representative part) while TM-08 does have a representative part. Revisit this guide's DISENO.md honesty table to answer precisely.

See solution

pre_invoke_checks.py (TM-09's primary mitigation) is pure, deterministic Python code that runs entirely offline, with no dependency on Bedrock at all — it can be tested end to end with pytest, and the result (PII FOUND, emails and phone numbers detected and redacted) is as literal as any other result in this ecosystem. TM-08, on the other hand, is primarily mitigated by Bedrock Guardrails' content_policy_config — a managed mechanism, that can only be confirmed actually blocking when the real model evaluates it —; the HCL declaration is real and executed, but its effectiveness against a real attack depends on invoking Bedrock, something this $0 lab never does (this guide's Module 4, lesson 7). The difference isn't in the control's quality, it's in how verifiable it is without needing a real invocation.

Exercise 3 — Design, in prose, a hypothetical TM-10 row this module did NOT cover, related to availability (not security) of the AI infrastructure. Justify why it wasn't necessary for this specific module's scope.

See solution

A reasonable answer: a TM-10 row could document the risk of extract-shipment-manifest-fields hitting Bedrock's per-minute invocation quota (named in this guide's Module 2, lesson 3) during a spike of free-text manifests, leaving manifests unprocessed until the quota frees up — a form of Denial of service specific to a usage-based service, different from TM-06 (which protects against destroying the Shipments table, not against a rate limit). It wasn't necessary for this module's scope because this module precisely limits itself to extending the inherited security gate — policy-check/iac-scan/verify-artifact — to the new infrastructure; a service-quota risk more naturally belongs to the reliability vocabulary (SLI/SLO) this guide's Module 7 builds, not to this module's security vocabulary.


Summary and next step

This lesson added TM-08 (Tampering, attempted prompt manipulation, mitigated by Module 4's content_policy_config) and TM-09 (Information disclosure, incidental PII leak, mitigated by two independent defense-in-depth layers) to THREAT-MODEL.md, without renumbering or touching any of cloud-security-and-guardrails-guide's seven original rows. You confirmed, with grep -c, that the document now has nine rows — and precisely saw why a generative AI workload needs exactly two new rows, not a complete rewrite of the STRIDE framework.

Before moving on you should be able to: cite the exact files that mitigate TM-08 and TM-09; explain why those two rows don't renumber the seven original ones; and distinguish TM-08 from TM-02 despite sharing a STRIDE category.

Lesson 8, the project that closes this module, runs the inherited security gate's three jobs — with this module's Terraform and artifact included — end to end, with a real act pull_request.

Resources

  1. cloud-security-and-guardrails-guide, Module 1, lesson 7 (07-hands-on-writing-the-threat-model-document.md) — the original document this lesson extends, with the seven rows and the maintenance section that authorizes this update.
  2. Microsoft Learn — Threats: Microsoft Threat Modeling Tool — STRIDE's definitions, the same source the sibling guide already cited.
  3. This guide's Module 4, lessons 3, 5, and 7 — the origin of each mitigation cited in TM-08 and TM-09.
  4. AWS Docs — Prompt injection — official reference for the prompt-attack detection mechanism TM-08 cites.