Security
Vector Expressions is designed with a zero-trust security posture. Expressions are author-controlled inputs evaluated server-side, so several hardened guardrails are built directly into the engine.
Who Can Write Expressions?
Only users with the edit_posts capability can save expression bindings to blocks. This maps to the WordPress Editor role and above by default. Subscribers and unauthenticated visitors cannot author or modify expressions. The REST API route used for editor previews enforces the same capability check.
Property Access Restrictions
Access to context data is restricted at multiple layers:
User object: Only an explicit allow-list of properties is accessible — id, name, email, roles, and a small set of curated aliases. Database-level fields like user_pass, user_activation_key, and capability maps (e.g., wp_capabilities) are not in the allow-list and are inaccessible by design.
Meta keys: Both post.meta and the get_meta modifier enforce two independent layers of protection:
-
WordPress convention — any key that
is_protected_meta()considers protected (i.e. keys starting with an underscore) is automatically denied. -
Sensitive-keyword scan —
ObjectProxy::is_sensitive_key()performs a substring match against a built-in list of sensitive keywords. Any meta key containing one of the following strings is blocked regardless of its prefix:pass,token,secret,api_key,auth,nonce,salt,credential,private_keyThis catches legitimately public-looking keys — e.g.
stripe_api_keyorauth_token— that the underscore convention alone would miss.The keyword list is filterable:
add_filter( 'vector_expressions/security/sensitive_meta_keywords', function( array $keywords ): array { $keywords[] = 'my_custom_secret_term'; return $keywords; } );
The vector_expressions/sanitization/deny_list filter controls HTML attribute injection (blocking attributes like onclick or srcdoc during block rendering), not property access.
Recursion Depth Limiting
The parser enforces a maximum nesting depth of 5. This prevents maliciously crafted or accidentally deeply nested expressions from causing stack overflows. When the limit is exceeded, the parser returns the raw template string as-is rather than an empty string or an error.
Output Escaping
The engine applies different escaping strategies depending on expression syntax:
| Syntax | Escaping |
|---|---|
{{ expr }} | htmlspecialchars() with ENT_QUOTES — prevents all HTML and attribute injection |
{{{ expr }}} | wp_kses_post() — allows safe HTML; strips dangerous tags like <script> |
| HTML attribute injection | WP_HTML_Tag_Processor handles encoding natively; recognized URL attributes (href, src) are additionally run through esc_url() to block javascript: URI injection |
The raw modifier follows the same wp_kses_post() path as triple-brace syntax.
Expression Sandboxing
The expression language does not allow:
- Arbitrary PHP function calls
- File system access
- HTTP requests (core engine only — Pro adds controlled REST integrations)
- Raw SQL queries
The parser grammar is strictly defined using a custom recursive-descent tokenizer. Any syntax outside the defined grammar is rejected and returns an empty string. All variable resolution and modifier dispatch run through controlled internal classes (Context and Library) — there is no eval() or dynamic function dispatch.
Best Practices
- Keep custom roots read-only. Context roots should expose data, not trigger side effects or mutations.
- Prefix sensitive custom meta with an underscore. The engine treats underscore-prefixed meta as protected automatically via
is_protected_meta(). For public-facing meta keys that nonetheless contain sensitive data (e.g.stripe_api_key), extend the keyword list via thevector_expressions/security/sensitive_meta_keywordsfilter rather than relying on the underscore convention alone. - Use the sanitization deny list for attributes. If you allow dynamic attribute binding, audit
vector_expressions/sanitization/deny_listto ensure dangerous attributes are blocked. - Avoid raw output unless necessary.
{{{ }}}and therawmodifier bypasshtmlspecialchars. Use them only for trusted content that you control.