Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: gblikas <13577108+gblikas@users.noreply.github.com>
- Update validateDenyValues to track negation context during AST traversal, allowing NOT/!=/NOT IN comparisons with denied values to pass validation - Add enforceExcludedValues security option that injects NOT IN filters at the adapter layer for server-side RBAC enforcement - Add 22 new tests covering negation handling, double/triple negation, complex AND/OR expressions, and enforceExcludedValues query injection Co-authored-by: gblikas <13577108+gblikas@users.noreply.github.com>
…e docs and tests - Revert validateDenyValues to original behavior: block all mentions of denied values regardless of operator (NOT, !=, NOT IN, etc.) - Update denyValues JSDoc to explain the explicit allow-listing design and when to use enforceExcludedValues instead - Replace incorrect negation-allowing tests with tests that correctly expect all negated forms to throw QuerySecurityError - Fix integration test that incorrectly expected NOT status:"archived" to pass denyValues validation Co-authored-by: gblikas <13577108+gblikas@users.noreply.github.com>
gblikas
left a comment
There was a problem hiding this comment.
@copilot tests are incomplete, and inconclusive.
Feedback
You've checked that generated SQL expressions (do not) have not in, but didn't check what the value of the not in corresponded to.
Further, you didn't check a critical test:
const qk = createQueryKit({
adapter,
schema: { todos: { id: {}, title: {}, priority: {}, status: {} } },
security: {
// denyValues blocks any query that references these values
denyValues: { status: ['archived', 'deleted'] },
// enforceExcludedValues injects NOT IN so those records never appear
enforceExcludedValues: { status: ['archived', 'deleted'] }
}
});Should check something like
await qk.query('todos').where('NOT status:deleted');our goal is to make sure that only status:active elements are returned, since we've denies archived and deleted.
…l denyValues+enforceExcludedValues interaction test
- All existing NOT IN tests now also assert the field name ('status') to verify the exclusion targets the correct column
- Added critical test: NOT status:deleted is blocked by denyValues; NOT status:published passes but gets status NOT IN ('archived', 'deleted') injected by enforceExcludedValues, ensuring archived/deleted records are never returned
Co-authored-by: gblikas <13577108+gblikas@users.noreply.github.com>
Fixed in commit
|
gblikas
left a comment
There was a problem hiding this comment.
@copilot generate more test cases for the integration tests, that more throughly test the relationship between denyValues and enforceExcludedValues. We should make sure that our solution is robust to "hacks" and various other SQL manipulations.
New test suite covers:
- denyValues blocking: direct equality, NOT negation for each denied value,
compound AND/OR with denied value, deep nesting, IN with mixed/all denied values
- enforceExcludedValues injection: status:active, title query, priority:>1,
compound AND/OR on safe fields, NOT status:published (key bypass attempt),
separate QK instance
Each 'safe' test verifies field name ('status'), operator ('not in'), and
both excluded values ('archived', 'deleted') in the generated SQL
Co-authored-by: gblikas <13577108+gblikas@users.noreply.github.com>
Added a new denyValues blocks all reference forms:
enforceExcludedValues always injects
Each "safe query" test asserts |
denyValueswas incorrectly modified to allow negated references to denied values (NOT status:"archived"). The original blocking behavior was correct — users should not be able to reference sensitive values in any form. The missing piece was a separate mechanism to ensure denied records never appear in results regardless of query phrasing.Changes
src/security/validator.tsvalidateDenyValuesto block all mentions of denied values, includingNOT field:"val",field != "val",field NOT IN [...], and any depth of negation nestingsrc/security/types.tsdenyValuesJSDoc: explicit allow-listing design, any reference to a denied value is rejected regardless of operatorenforceExcludedValues— e.g.NOT status:"published"implicitly includesarchived/deletedrows, whichdenyValuesalone cannot preventsrc/index.ts/enforceExcludedValuesenforceExcludedValuesoption introduced in the prior session — this is the correct RBAC enforcement toolAND field NOT IN (values)after validation so denied records never appear, regardless of how the user phrases their queryTests
validator.test.ts: all negated forms now assertQuerySecurityErrorNOT status:"archived"to passdenyValues— it should throwenforceExcludedValuesintegration tests to assert field-level correctness: tests now verify theNOT INclause targets the correct column (e.g.status) and the exact excluded values, not just that "not in" appears somewhere in the SQLdenyValues+enforceExcludedValuesboth configured,NOT status:deletedis blocked bydenyValues;NOT status:publishedpasses validation but getsAND status NOT IN ('archived', 'deleted')injected, ensuring only non-excluded records (e.g.status:active) can ever be returneddenyValues+enforceExcludedValuescombinationOriginal prompt
Summary
Fix issue #17:
denyValuesblocks queries with negated values (NOT operator)GitHub Issue: #17
Problem
The
denyValuessecurity feature currently blocks any query that mentions a denied value, including queries that explicitly exclude that value using negation (NOT,!=,NOT IN).Current Behavior
status:"archived"→ Blocked ✅status:"deleted"→ Blocked ✅NOT status:"archived"→ Blocked ❌ (should be allowed)status != "deleted"→ Blocked ❌ (should be allowed)Expected Behavior
status:"archived"→ Blocked (user trying to access archived items)status:"deleted"→ Blocked (user trying to access deleted items)NOT status:"archived"→ Allowed (user explicitly excluding archived items)status != "deleted"→ Allowed (user explicitly excluding deleted items)Solution
Implement two features:
1. Negation Context Fix for
denyValuesModify
validateDenyValuesinsrc/security/validator.tsto track negation context during AST traversal:Key points:
negated: boolean = falsethrough recursive calls!negatedwhen hittingNOToperator (handles double-negation correctly)!=andNOT INoperators as exclusions2. New
enforceExcludedValuesOptionAdd a new security option that automatically injects exclusion filters into every query at the adapter layer. This ensures sensitive records are never returned regardless of how users phrase their queries.
Add to
src/security/types.ts:Implement the query transformation that applies these exclusions before SQL generation.
Files to Modify
src/security/validator.ts: UpdatevalidateDenyValuesmethod with negation contextsrc/security/types.ts: AddenforceExcludedValuesoption and update documentation fordenyValuessrc/security/validator.test.ts: Add extensive tests for negation handlingRequired Tests
Add the following test cases to
src/security/validator.test.ts: