Expression evaluation security review with non-subtractive guardrails (#500)#984
Merged
Conversation
… guardrails (#500) - New engineering/EXPRESSION_SECURITY.md: threat model, input path inventory, verified DynamicExpresso exposure/blocking, guardrails, rejected measures, and one documented open finding (bare GetType() returns an inert Type; all chained reflection is blocked) - DynamicExpressoEvaluator: 10,000-character expression length ceiling and a 1,000-entry bound on the static compiled-expression cache (previously unbounded via the ad-hoc expression tester); both TDD - 28 security proof tests (reflection/type escapes, hostile-looking inputs) and a 46-test functionality regression suite proving every built-in function is unaffected by the guardrails - OWASP assessment moved to engineering/plans/doing/ with gap 5 marked implemented; COMPLIANCE_MAPPING updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7 tasks
…curity-review # Conflicts: # engineering/plans/doing/OWASP_TOP_10_ASSESSMENT.md
…curity-review # Conflicts: # engineering/COMPLIANCE_MAPPING.md
…curity-review # Conflicts: # engineering/COMPLIANCE_MAPPING.md
JayVDZ
added a commit
that referenced
this pull request
Jul 14, 2026
#1014) All five remediation gaps are now merged (#1001 rate limiting, #1002 security headers/CSP, #983 NuGet lock files, #984 expression security review, #1013 security audit events), completing the assessment's remediation programme. Move the assessment document to plans/done per the plan filing convention, set its status to Done with the deferred-items note, and repoint the three engineering documents that referenced the plans/doing path. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the "DynamicExpresso input path review" gap from the OWASP Top 10:2025 assessment (A03/A05, gap 5). Part of #500.
The deliverable is a documented security review,
engineering/EXPRESSION_SECURITY.md, plus two strictly non-subtractive guardrails. Per the product owner's constraint, nothing in this PR changes what any legitimate expression can do; a 46-test functionality regression suite (expressions taken verbatim from the customer docs, covering every built-in function) proves it.Review findings
SyncRuleMappingSource.Expressionis live today (Administrator-only via the REST API, the Attribute Flow tab, andNew-JIMSyncRuleMapping).ObjectMatchingRuleSource.ExpressionandExampleDataTemplateAttribute.Expressionare model-ready but have no reachable authoring surface yet (verified: no DTO/UI editor/cmdlet populates them, and seeded templates carry no expressions); they inherit the evaluator's guardrails automatically if wired up later.mv/csaccessors as data, never as expression source; hostile-looking values are proven inert..GetType().GetMethods(),.Assembly,typeof(double).GetMethods()) throwsReflectionNotAllowedException; unregistered types (Process,FileStream,Environment,Activator,AppDomain) fail as unknown identifiers; assignment has no settable target.Validaterejects everythingEvaluaterejects, so protection applies at save-time too.mv["a"].GetType()parses and evaluates, returning an inertSystem.Type(DynamicExpresso's reflection blocker rejects anything chained onto aType, andType.Nameis its one documented allowed member). Chaining is blocked, the value grants no capability, the author is a trusted Administrator, and blocking it would need brittle custom parse inspection; documented in the review rather than hardened.Guardrails added (both TDD, red first)
MaxExpressionLength(10,000 characters) acrossEvaluate/Validate/Test: a paste-bomb sanity bound roughly two orders of magnitude above the longest documented real expression.MaxCompiledExpressionCacheSize(1,000 entries) on the previously unbounded static compiled-expression cache, which ad-hoc expression-tester usage grew forever; clear-and-log on reaching the bound (recompilation is sub-millisecond).Rejected measures are recorded in the review with rationale: function/operator allowlists, removing default reference types, and evaluation timeouts all impair legitimate or future transformation capability for no matching risk reduction against a trusted-author threat model.
Validation
dotnet build JIM.sln: 0 errors, 0 warnings.dotnet test JIM.sln: 3,436 passed (+80 new), 35 skipped (category-gated), 0 failed.Note: this branch and #983 both move the OWASP assessment doc to
plans/doing/and touch the same COMPLIANCE_MAPPING row; whichever merges second will be brought up to date withmainand reconciled before merge.Docs: n/a - internal security review; the changelog entry is 🔒 and behaviour is unchanged for administrators.
🤖 Generated with Claude Code