Skip to content

fix(cel): recognise 'me' as a context identifier (dotted DCI validation false-positive) - #342

Draft
gonzalesedwin1123 wants to merge 6 commits into
19.0from
security-dci-cel-me-validation
Draft

fix(cel): recognise 'me' as a context identifier (dotted DCI validation false-positive)#342
gonzalesedwin1123 wants to merge 6 commits into
19.0from
security-dci-cel-me-validation

Conversation

@gonzalesedwin1123

@gonzalesedwin1123gonzalesedwin1123 commented Jul 24, 2026

Copy link
Copy Markdown
Member

Do not merge yet — held for human review.

Summary

validate_expression() and validate_formula_expression() wrongly rejected valid dotted DCI CEL expressions with Undefined variables: me.

Root cause: the DCI resolver override (cel_variable_resolver_dci.py) rewrites a dotted cached accessor such as r.dci.crvs.is_alive into metric('r.dci.crvs.is_alive', me) in a top-level pre-pass, before the base resolver extracts identifiers. The base _extract_variable_names then lexes that rewritten string and returns the bare me (an IDENT outside the quoted accessor) as a potential variable. me was missing from CEL_CONTEXT_IDENTIFIERS (cel_parser.py had m, e, r but not me), so it wasn't in the resolver's reserved words → looked up as a variable → not found → reported as a missing/undefined variable. compile_expression() was unaffected because it does not reject missing_variables, which is why enrollment/eligibility worked while validation-only callers (UI checks, API) failed.

me is a genuine context identifier, not a typo:

  • bound in the eval namespace — spp_programs/models/cel/entitlement_amount_cel.py:299"me": SafeRecordProxy(beneficiary);
  • treated as a record root — spp_cel_domain/services/cel_predicate_guard.py:35_RECORD_ROOTS = ("r", "me");
  • emitted by the base resolver itself — metric('<var>', me). It previously only ever appeared in replacement output, never in a scanned expression, so only the DCI dotted path tripped.

Fix

One line in the source layer: add "me" to CEL_CONTEXT_IDENTIFIERS in spp_cel_domain/services/cel_parser.py. That set feeds reserved_words in both consumers (cel_variable_resolver and cel_expression), so the first-pass filter skips me everywhere. Fixes all callers, not just DCI — chosen over patching the DCI module, which would leave the same latent gap for any other caller that puts me in a scanned expression. Version bump spp_cel_domain 19.0.2.1.0 → 19.0.2.1.1. No migration (a constant change; no schema/data).

Tests (TDD)

  • spp_cel_domain (tests/test_cel_me_identifier.py): me is in CEL_CONTEXT_IDENTIFIERS; expand_expression / validate_expression on an expression containing bare me (metric('foo', me) == true) does not flag it; cel.service.validate_formula_expression likewise. Red before the fix, green after.
  • spp_dci_indicators (tests/test_dci_cel_validation.py, test-only, no version bump): the seeded dotted accessor r.dci.crvs.is_alive expands to metric('r.dci.crvs.is_alive', me) and validate_expression("r.dci.crvs.is_alive == true") is valid with no Undefined variables: me — the exact reported scenario.
  • Green: spp_cel_domain 652/652, spp_dci_indicators 84/84; ruff/ruff-format clean. Verified no spp.cel.variable is literally named/accessed me (adding the reserved word only removes such a name from variable lookup; me would be an illegal name anyway since it is the record root).

Merge order (spp_cel_domain pair)

Per the cross-PR interaction analysis (2026-07-24, internal/plans/security-prs-interaction-analysis.md): merge after #272. Both PRs bump spp_cel_domain to the identical19.0.2.1.1, and that manifest line auto-merges with no conflict — so this PR's post-#272 rebase must deliberately:

  1. Re-bump spp_cel_domain to 19.0.2.1.2;
  2. Move this PR's HISTORY bullet under a new ### 19.0.2.1.2 heading above security(cel): key metric cache lookups strictly by requested params #272's ### 19.0.2.1.1;
  3. Resolve the trivial keep-both conflict in spp_cel_domain/tests/__init__.py;
  4. Let CI's oca-gen regenerate README.rst/index.html.

Code paths are provably disjoint (this PR: validation identifier set; #272: execution cache keying) — no semantic interaction.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@codecov

codecovBot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.29%. Comparing base (208d975) to head (0480f16).

Additional details and impacted files

Impacted file tree graph

@@ Coverage Diff @@## 19.0 #342 +/- ##
==========================================
+ Coverage 70.17% 70.29% +0.12% 
==========================================
Files 205 367 +162 Lines 17751 29533 +11782 ==========================================
+ Hits 12457 20761 +8304 - Misses 5294 8772 +3478 
FlagCoverage Δ
spp_analytics93.25% <ø> (?)
spp_api_v2_change_request66.53% <ø> (ø)
spp_api_v2_cycles71.03% <ø> (?)
spp_api_v2_data77.77% <ø> (?)
spp_api_v2_entitlements70.23% <ø> (?)
spp_api_v2_gis71.57% <ø> (?)
spp_api_v2_programs92.22% <ø> (?)
spp_api_v2_simulation71.19% <ø> (?)
spp_approval50.34% <ø> (?)
spp_base_common91.07% <ø> (ø)
spp_case_cel89.50% <ø> (?)
spp_case_entitlements100.00% <ø> (?)
spp_case_programs100.00% <ø> (?)
spp_cel_domain63.79% <ø> (?)
spp_cel_event85.34% <ø> (?)
spp_dci_indicators96.23% <ø> (?)
spp_programs65.27% <ø> (ø)
spp_registry87.22% <ø> (+0.07%)⬆️
spp_security69.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing linesCoverage Δ
spp_cel_domain/models/cel_variable_resolver.py78.15% <ø> (ø)
spp_cel_domain/services/cel_parser.py82.44% <ø> (ø)

... and 161 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…iable
TDD red: the DCI resolver rewrites dotted accessors to metric('...', me)
before the base resolver extracts identifiers, and 'me' is missing from
CEL_CONTEXT_IDENTIFIERS, so validate_expression/validate_formula_expression
report 'Undefined variables: me' for valid dotted DCI expressions. New tests
assert me is recognised (base resolver + cel.service formula path + DCI
dotted-accessor end-to-end).
'me' is the individual record proxy in the CEL eval context (bound as
'me': SafeRecordProxy(beneficiary) and treated as a record root in
cel_predicate_guard), and the resolver emits metric('<accessor>', me) for
cached/dotted variables. Because 'me' was missing from
CEL_CONTEXT_IDENTIFIERS, once the DCI override rewrites a dotted accessor
into metric('...', me) before identifier extraction, validate_expression /
validate_formula_expression reported valid expressions as
'Undefined variables: me' (compile_expression was unaffected as it does
not reject missing variables). Add 'me' to CEL_CONTEXT_IDENTIFIERS so it
is skipped as a reserved context identifier in both consumers
(resolver + cel_expression). Bump spp_cel_domain to 19.0.2.1.1.
…d set
Staff-review nit: the fallback set used when the cel_parser import fails
omitted 'me', so the fix would not hold on that (catastrophic) path.
Mirror 'me' into the fallback for consistency.
…with #272
Both this PR and #272 claimed 19.0.2.1.1. Identical manifest lines auto-merge
with no conflict, so whichever landed second would have shipped with no
effective version bump — the module would not upgrade and the fix would not
deploy. Everything around the manifest (README, HISTORY, index.html,
tests/__init__.py) does conflict, which is what makes the version the one
part nothing flags.
@gonzalesedwin1123
gonzalesedwin1123force-pushed the security-dci-cel-me-validation branch from 35c2814 to d8e834cCompareAugust 10, 2026 08:46
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gonzalesedwin1123