Product Decisions This Supports
- Whether to ship user/tenant-authored templates at all. Features like customer-editable email and notification templates, CMS blocks, white-label theming, or low-code automation steps all require executing untrusted Twig in a sandbox. A sandbox is the gate; this package lowers the cost of building that gate, so it directly affects whether such a feature is feasible in the next cycle.
- Build vs. buy on the allowlist mechanism. Rather than hand-maintaining a YAML/PHP allowlist of "safe" methods and properties for every domain object, exposure is declared via annotations on the classes themselves. That's a meaningful reduction in bespoke security scaffolding — often the slowest part of these projects.
- Fail-open vs. fail-closed policy — now a configurable decision. v3.4.0 adds a strict mode to the sandbox validator. This matters more than it sounds: the classic failure of an annotation-driven allowlist is the forgotten annotation — a new domain method ships, nobody marks it, and whether it becomes template-reachable depends on how the policy treats unmarked members. Strict mode lets us choose "unmarked means denied," which turns exposure into an explicit opt-in instead of an implicit default. That is the difference between a security posture you assert and one you assume.
- Scope: how much of the domain model templates may touch. Annotations make "this method is template-visible" an explicit, code-adjacent decision owned by the domain team and visible in code review, instead of a drifting central config file. That's a governance improvement you can point at in design reviews — and strict mode gives that governance a default-deny backstop.
- Roadmap sequencing. If template editing is a differentiator, adopting an annotation-driven policy lets you pull the feature forward instead of spending a sprint or two on allowlist plumbing. Strict mode removes the "we'll harden it later" tail of that work.
- Support and maintenance cost. Colocating the allowlist with the code reduces "can I use X in a template?" tickets and makes the answer discoverable by solutions engineers, not just the platform team.
- Compliance/security narrative. It gives a defensible answer to "how do you stop a customer template from reading
User::getPassword() or calling arbitrary methods?" — sandbox, an explicit allowlist, and (with strict mode on) a documented default-deny rule for anything not deliberately exposed.
When to Consider This Package
Consider it when all of these hold:
- You are on Symfony with Twig and are actually enabling
Twig\Sandbox for untrusted templates. (Note: this is a Symfony bundle, not a Laravel package — on Laravel/Blade it does not apply, and on Laravel-with-Twig you'd still need to wire a policy yourself. Don't let a "PHP package" framing blur that.)
- Your sandbox allowlist is large or frequently changing, i.e., the maintenance burden this package removes actually exists.
- You want exposure decisions owned by domain teams via annotations/attributes, not centralized config.
- You will run the validator in strict mode and confirm what it does to unannotated members before you rely on it. v3.4.0 is a minor release, so strict mode is almost certainly opt-in rather than a silent behavioral change — verify the flag, its default, and that enabling it doesn't break currently-working templates in your test suite. Adopting the package but leaving strict mode off forfeits the main security argument for adopting it at all.
- You can tolerate a small, low-adoption dependency in a security-sensitive path — pin the version, vendor or fork it, and write tests that assert the policy denies what it should.
Look elsewhere or decline when:
- The allowlist is small and static (a dozen methods). A hand-written
SecurityPolicy is a few hundred lines you fully own and understand — cheaper than a dependency. (Strict mode narrows this gap somewhat, since it reduces the "we'll forget one" risk that pushes teams toward tooling, but it doesn't change the math on a dozen methods.)
- You need active maintenance, CVE response, or vendor support. 7 stars, 0 dependents, a single maintainer, and a security-critical role is a poor combination for a critical path. One new feature does not change the maintenance profile.
- Your coding standards forbid docblock annotations. Verify it supports PHP 8 attributes (the modern equivalent) before committing; the package's premise is annotation-based config.
- High-assurance / multi-tenant isolation is a hard requirement. Twig's sandbox is a policy layer, not a process boundary — it restricts which methods/properties are reachable, not what those methods do. Strict mode tightens the allowlist; it does not isolate tenants. Pair it with resource limits and treat it as defense-in-depth.
- Sandboxing is only for developer-authored templates. The sandbox has a runtime cost and buys you nothing there.
How to Pitch It (Stakeholders)
To executives: This is a small, MIT-licensed building block that lets us offer customer-editable templates — email, notifications, theming — without opening a code-execution hole. It shortens time-to-market by removing hand-maintained security allowlists, and the latest release adds a strict mode so that anything not deliberately exposed is denied by default — that moves our answer from "we think we've covered everything" to "we can show the rule." The risk remains dependency maintenance, not capability; the surface area is small enough that pinning or forking it is a realistic mitigation.
To engineering: Annotation/attribute-driven SecurityPolicy for Twig's sandbox. It replaces a central allowlist with per-class declarations, so exposure decisions live with the domain code and show up in PR review. v3.4.0 adds a strict mode on the sandbox validator — enable it and treat default-deny as the intended configuration; confirm the flag name and default, and check it against existing templates before rollout. Caveats to accept up front: Symfony-only, tiny community, zero downstream dependents, and it sits in a security path — so pin the version, add negative tests proving denied access stays denied (including an unannotated method), and budget for a fork if it goes stale. If our allowlist stays small, we should just write the policy ourselves.