Hacker DirectoryHacker Directory

WEB / WEB-18.S01

Query/data separation in SQL and NoSQL

Follow typed intents, application-owned queries, and projected results in a public catalog.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Bound SQL title

  1. Catalog intent ready

    The caller’s catalog intent carries backend=sql; title=O'Reilly; sort=id. The query builder will validate these fields; no statement/filter or completed row result exists yet.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    No query contract
    Data owner
    No lookup; count=0
    Result view
    No result
  2. Field-type decision

    The closed backend/title/sort keys, backend enum and scalar title pass. Title is within 1–40 code points; a string sort still needs the allowed-value decision at the next stage.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    Fields typed
    Data owner
    No lookup; count=0
    Result view
    No result
  3. Query-contract selection

    sort=id selects the application’s fixed by-id SELECT. O'Reilly/public form its bound title/visibility tuple; these values are not concatenated into statement syntax.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    by-id + bound values
    Data owner
    No lookup; count=0
    Result view
    No result
  4. Local-lookup decision

    The local SQLite SELECT completes: title=O'Reilly and visibility=public select id=2 with queries=1. Seeds remain unchanged and the result view has not consumed the projection yet.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    by-id + bound values
    Data owner
    Lookup complete; count=1; rows=1
    Result view
    No result
  5. Projection ready

    Completed rows are projected to id/title only; rejected branches retain null rows while the consumer still has no result.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    by-id + bound values
    Data owner
    Projected rows=1; count=1
    Result view
    No result
  6. Result-view decision

    The view consumes only a completed projected result; rejection is not shown as success or an empty match and the seed is unchanged.

    Input facts
    backend=sql; title=O'Reilly; sort=id
    Query builder
    by-id + bound values
    Data owner
    Projected rows=1; count=1
    Result view
    Public result: id=2, title=O'Reilly

Scalar filter

  1. Catalog intent ready

    The caller’s catalog intent carries backend=document; title=Tea; sort=id. The query builder will validate these fields; no statement/filter or completed row result exists yet.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    No query contract
    Data owner
    No lookup; count=0
    Result view
    No result
  2. Field-type decision

    The closed backend/title/sort keys, backend enum and scalar title pass. Title is within 1–40 code points; a string sort still needs the allowed-value decision at the next stage.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    Fields typed
    Data owner
    No lookup; count=0
    Result view
    No result
  3. Query-contract selection

    The application constructs {title:Tea,visibility:public} equality filtering from the admitted Tea string and selects sort=id. It does not copy a caller object as the filter; this narrow matcher is not MongoDB.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    Scalar public filter
    Data owner
    No lookup; count=0
    Result view
    No result
  4. Local-lookup decision

    The local literal matcher finds id=1 and id=3 for title=Tea and visibility=public, ordering them by id. queries=1 with two matches; the view has not consumed this result yet.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    Scalar public filter
    Data owner
    Lookup complete; count=1; rows=2
    Result view
    No result
  5. Projection ready

    Completed rows are projected to id/title only; rejected branches retain null rows while the consumer still has no result.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    Scalar public filter
    Data owner
    Projected rows=2; count=1
    Result view
    No result
  6. Result-view decision

    The view consumes only a completed projected result; rejection is not shown as success or an empty match and the seed is unchanged.

    Input facts
    backend=document; title=Tea; sort=id
    Query builder
    Scalar public filter
    Data owner
    Projected rows=2; count=1
    Result view
    Public result: Tea, id=1 and id=3

Object title

  1. Catalog intent ready

    The caller’s catalog intent carries backend=document; title={display:Tea}; sort=id. The query builder will validate these fields; no statement/filter or completed row result exists yet.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    No query contract
    Data owner
    No lookup; count=0
    Result view
    No result
  2. Field-type decision

    title={display:Tea} can be a valid plain object but fails title:string. The reason is field-type; sort checking, query construction and data-owner lookup do not begin.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    Rejected: field-type
    Data owner
    No lookup; count=0
    Result view
    No result
  3. Query-contract selection

    After type admission the sort enum is chosen; unknown sort emits no statement and failed types skip sort evaluation.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    Sort not evaluated
    Data owner
    No lookup; count=0
    Result view
    No result
  4. Local-lookup decision

    This denied branch invokes neither the SQLite driver nor the literal document matcher. queries=0 is not the empty result of an executed query; no earlier result is reused for this intent.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    Sort not evaluated
    Data owner
    No lookup; count=0
    Result view
    No result
  5. No projection produced

    Without a lookup there is no projection, so rows remains null. This differs from rows=[] after an admitted Bread search with no match; seed data stays unchanged.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    Sort not evaluated
    Data owner
    Rows null; count=0
    Result view
    No result
  6. Result-view decision

    The view consumes only a completed projected result; rejection is not shown as success or an empty match and the seed is unchanged.

    Input facts
    backend=document; title={display:Tea}; sort=id
    Query builder
    Sort not evaluated
    Data owner
    Rows null; count=0
    Result view
    No result; field-type

Unknown sort

  1. Catalog intent ready

    The caller’s catalog intent carries backend=sql; title=Tea; sort=rating. The query builder will validate these fields; no statement/filter or completed row result exists yet.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    No query contract
    Data owner
    No lookup; count=0
    Result view
    No result
  2. Field-type decision

    Closed keys and scalar title/string types are checked; an ordinary display object does not become an admitted title.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    Fields typed
    Data owner
    No lookup; count=0
    Result view
    No result
  3. Query-contract selection

    Backend and scalar-title types are valid, but sort=rating is outside the allowed id/title enum. The reason is unknown-sort; statementId=null, filter=null and queries=0 remain.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    Rejected: unknown-sort
    Data owner
    No lookup; count=0
    Result view
    No result
  4. Local-lookup decision

    This denied branch invokes neither the SQLite driver nor the literal document matcher. queries=0 is not the empty result of an executed query; no earlier result is reused for this intent.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    Rejected: unknown-sort
    Data owner
    No lookup; count=0
    Result view
    No result
  5. No projection produced

    Without a lookup there is no projection, so rows remains null. This differs from rows=[] after an admitted Bread search with no match; seed data stays unchanged.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    Rejected: unknown-sort
    Data owner
    Rows null; count=0
    Result view
    No result
  6. Result-view decision

    The view consumes only a completed projected result; rejection is not shown as success or an empty match and the seed is unchanged.

    Input facts
    backend=sql; title=Tea; sort=rating
    Query builder
    Rejected: unknown-sort
    Data owner
    Rows null; count=0
    Result view
    No result; unknown-sort

ACTORS · STATE · DATA FLOW

Query structure and search values have different owners

Atlas public catalog offers exact title matching and two approved sort choices. The caller supplies a book title; the application chooses the table, fields, comparison, and projection. O'Reilly is an ordinary title. Its apostrophe does not make it invalid business input. In this example the SQL statement stays fixed while title and public visibility are separate bound values passed to the driver. The book name is never assembled as a piece of statement syntax.

The query/data distinction starts before interpretation. The handler produces a typed intent containing only backend, title, and sort. Backend is a sql/document enum for this teaching profile; a real product need not let clients choose database backends. Title is a string of 1–40 code points and sort is id or title. That domain contract does not replace parameterization. Validation decides which operation is admissible; binding preserves the role of an admitted value inside the query.

The SQL driver receives two distinct arguments

The artifact contains two prewritten SELECT statements, by-id and by-title. Both specify the id/title projection and equality conditions for title and visibility. Python sqlite3 execute receives the statement and the (title, public) tuple separately. The native check opens its own :memory: database, inserts three ordinary catalog rows, and reads only id=2 for O'Reilly. Input admission is not query execution; the view cannot show a result row before execute completes.

With Python qmark binding, the sequence length must match the placeholder count. Named placeholders expect a mapping. These are driver contracts, separate from field validation and resource authorization. The value slots of the two fixed statements do not change. The animation does not guess every internal prepare/cache operation or another SQL engine’s dialect. The evidence belongs to the selected SQLite invocation and its observed row list.

Sorting is not a value slot

A title value and an ORDER BY column choice are different responsibilities. Putting title in a bound value slot does not turn it into a column identifier. The application maps id and title to its own two fixed statements. An unknown sort such as rating is rejected before executing a query. This profile admits no arbitrary identifier, raw SQL fragment, or direction string. Choosing a sort option does not delegate authorship of the query language.

A form’s appearance cannot prove this distinction. A title from a text box may be bound as data, while a sort from a dropdown still needs a server-side enum check. Two UI options are not evidence of the server contract. In the unknown-sort branch the selected statement stays null, the data owner records query count=0, and the result view has no result. Missing intent is a separate NOT-COMPUTED state; unknown and missing are different decisions.

A document filter also belongs to a query language

The NoSQL label does not remove an interpretation boundary. The application validates a scalar title before constructing its own equality filter. Passing a client object through as the filter confuses application-owned query structure with client-owned data structure. Here the ordinary {display: Tea} object does not satisfy title:string and is rejected before any document lookup. Being a parsed object does not make it an admitted title value.

The document branch is a narrow Python literal matcher: application-built title and visibility fields select exact matches from its own seed documents, sort them by id or title, and project id/title. This matcher is not a MongoDB server, BSON codec, cursor, or ODM. MongoDB find returns a cursor; the example’s ready list does not reproduce all cursor behavior. The animation’s local Data owner responsibility keeps that limit explicit.

A correct query still needs the right access decision

Binding preserves query structure; it does not decide which row may be disclosed to which caller. This catalog uses fixed teaching data selected for public visibility. Private resources, tenant membership, service account privileges, and field-level disclosure require separate policy in a real application. The public scope condition is application-owned; no new scope decision is inferred from the title field.

An accepted query returning an empty row list is also different from rejection. Type/schema failure, unknown sort, driver failure, and an accepted lookup with no match need distinct outcomes. The result view changes only after lookup and projection complete. This distinction informs retry and error handlers: reporting a lookup that never ran as an empty result hides what the application actually checked. This artifact performs only SELECT/literal lookup, with no mutation, network, or production authorization.

2. Red · Trust & Impact

Read every scenario without motion

Field type

  1. The query-intent profile was read

    The document backend expects title as a scalar string. The wrong-type input has title={display:Tea}; this ordinary object may be valid JSON without being an accepted title type.

    Caller intent
    Backend: document
    Typed query factory
    Intent validation pending
    Data-owner boundary
    Query not executed
  2. Value and structure responsibilities were separated

    A parsed object is not copied directly into a query filter. The application builds a new equality filter from a typed title; an unaccepted type never reaches that factory.

    Caller intent
    Application selects structure
    Typed query factory
    Field-type mismatch
    Data-owner boundary
    Factory gate pending
  3. Intent-gate denial was established

    The expected decision is REJECT with reason=field-type. No accepted filter or statement is prepared and queries=0 remains.

    Caller intent
    Decision: REJECT
    Typed query factory
    Queries=0
    Data-owner boundary
    Rows=null
  4. No dispatch reached the data owner

    statementId=null and filter=null. The driver or document matcher is not described as executing input and then concealing a result.

    Caller intent
    No accepted query structure
    Typed query factory
    No data-owner call
    Data-owner boundary
    No result delivery
  5. Failure and no-match were separated

    rows=null represents intent failure. The acceptedNoMatch search for Bread has ACCEPT, queries=1 and rows=[]; an empty result requires a performed read.

    Caller intent
    Rejection: rows=null
    Typed query factory
    Accepted no-match: rows=[]
    Data-owner boundary
    Outcomes distinct
  6. The denial class was not expanded incorrectly

    These ordinary type/sort denials show a boundary contract. They are not executable injection, endpoint-vulnerability or actor-permission-failure outcomes.

    Caller intent
    Contract rejection known
    Typed query factory
    No query execution
    Data-owner boundary
    Permission separate

Sort unknown

  1. The query-intent profile was read

    The SQL backend allows only id or title for sort. The unknown-sort input has sort=rating; the client choice is compared with the approved statement set.

    Caller intent
    Backend: sql
    Typed query factory
    Intent validation pending
    Data-owner boundary
    Query not executed
  2. Value and structure responsibilities were separated

    A sort choice is not solved by binding an identifier as a value parameter. The application maps id/title to two fixed statements without creating new SQL for rating.

    Caller intent
    Application selects structure
    Typed query factory
    Sort outside enum
    Data-owner boundary
    Factory gate pending
  3. Intent-gate denial was established

    The expected decision is REJECT with reason=unknown-sort. With no approved statement selected, queries=0 remains; the result is not presented as an empty list.

    Caller intent
    Decision: REJECT
    Typed query factory
    Queries=0
    Data-owner boundary
    Rows=null
  4. No dispatch reached the data owner

    statementId=null and filter=null. The driver or document matcher is not described as executing input and then concealing a result.

    Caller intent
    No accepted query structure
    Typed query factory
    No data-owner call
    Data-owner boundary
    No result delivery
  5. Failure and no-match were separated

    rows=null represents intent failure. The acceptedNoMatch search for Bread has ACCEPT, queries=1 and rows=[]; an empty result requires a performed read.

    Caller intent
    Rejection: rows=null
    Typed query factory
    Accepted no-match: rows=[]
    Data-owner boundary
    Outcomes distinct
  6. The denial class was not expanded incorrectly

    These ordinary type/sort denials show a boundary contract. They are not executable injection, endpoint-vulnerability or actor-permission-failure outcomes.

    Caller intent
    Contract rejection known
    Typed query factory
    No query execution
    Data-owner boundary
    Permission separate

ASSUMPTION · BOUNDARY · CONTROL

The review question is when a value became structure

Begin a review with responsibility rather than the visual appearance of input: where does the application choose the statement/filter, which fields can the caller influence, and which typed values actually reach the driver? Ordinary O'Reilly shows that punctuation can remain business data. It is not an attack-string exercise. The fixed SELECT and separate tuple expose which code owns the query structure.

Knowing that an ORM is used does not establish the contract of every call path. A model method, raw-expression escape hatch, and sort/projection factory may have different contracts. No raw expression executes in this lesson; the review asks whether the approved factory is used. Statement identifier, validated field types, and selected scope make useful review evidence. Dumping every input value into logs can disclose data without helping establish those boundaries.

Parsed objects and admitted filters are separate states

When title receives the ordinary display object, the transition from parsed JSON to business type stops. No query reaches the data owner and an earlier row list cannot masquerade as new success. This inert failure teaches the difference between parser acceptance and operation admission. Query count=0 together with a null result is the evidence; an error message by itself is insufficient.

An empty result needs evidence that an accepted lookup actually ran. If unknown sort rating were represented by the same empty list, contract failure would disappear. Missing evidence must not silently become denial or permission either. These narrow checks involve no endpoint discovery, injection payload, authorization bypass, or vulnerability reproduction on another system. They demonstrate the facts a boundary review can legitimately rely on.

3. Blue · Secure development & defense

Read every scenario without motion

Value binding

  1. The normal public intent was selected

    The normal SQL input has title=O'Reilly and sort=id. Punctuation belongs to business data and is not used to select query syntax.

    Intent/query observation
    Title: O'Reilly
    Factory/result comparison
    Sort: id
    Evidence conclusion
    Factory review pending
  2. The fixed-statement identity was matched

    sort=id selects the by-id statement. It uses title=? and visibility=?; title and public scope remain separate bound values.

    Intent/query observation
    Statement: by-id
    Factory/result comparison
    Values: O'Reilly / public
    Evidence conclusion
    Execution pending
  3. The expected query result was read

    The normal expected record has queries=1 and rows=[{id:2,title:O'Reilly}]. This positive outcome preserves punctuation as a value without validating every query path.

    Intent/query observation
    Queries=1
    Factory/result comparison
    Row id=2
    Evidence conclusion
    Title preserved
  4. Response-projection fields were separated

    The profile projection is only id and title. Visibility is used as a public-scope condition; copying every stored field into the response is not required.

    Intent/query observation
    Scope: public
    Factory/result comparison
    Projection: id/title
    Evidence conclusion
    No extra stored fields
  5. The document-branch factory was compared

    For document input title=Tea, the application creates an equality filter {title:Tea,visibility:public}. Building a new filter from an accepted string differs from copying the caller object.

    Intent/query observation
    SQL fixed statement
    Factory/result comparison
    Document fixed equality filter
    Evidence conclusion
    Factories explicit
  6. Binding was not converted into permission

    Parameter binding helps preserve the value/structure boundary. Tenant authorization, database-account permissions and production query timeout require separate controls beyond this public fixture.

    Intent/query observation
    Value boundary known
    Factory/result comparison
    Public scope fixed
    Evidence conclusion
    Authorization separate

No-match / missing

  1. The accepted-no-match record was opened

    acceptedNoMatch for title=Bread declares decision=ACCEPT and queries=1. rows=[] means the search was accepted and performed but found no match.

    Intent/query observation
    Intent accepted
    Factory/result comparison
    Queries=1
    Evidence conclusion
    Rows=[]
  2. Missing input was treated as a different tuple

    missing.input=null has decision=NOT-COMPUTED, queries=0 and rows=null. With no search performed, it is not interpreted like Bread’s empty result.

    Intent/query observation
    Input=null
    Factory/result comparison
    Queries=0
    Evidence conclusion
    Rows=null
  3. Known rejection and missing input were separated

    wrong-type and unknown-sort have REJECT while missing has NOT-COMPUTED. All can have no query, but their evaluated contract conditions differ.

    Intent/query observation
    Known rejection distinct
    Factory/result comparison
    Missing intent unresolved
    Evidence conclusion
    No-query cause explicit
  4. Query count was linked with results

    An accepted match needs queries=1 and projected rows; no-match expects the same query count with []. A UI error or empty view alone cannot complete this tuple.

    Intent/query observation
    Intent + query + rows
    Factory/result comparison
    UI alone insufficient
    Evidence conclusion
    Evidence tuple required
  5. The document result was read narrowly

    The Tea equality filter returns projected rows for id=1 and id=3. This literal-matcher result does not observe actual MongoDB/ODM behavior or every filter operator.

    Intent/query observation
    Document rows: 1, 3
    Factory/result comparison
    Literal matcher scope
    Evidence conclusion
    ODM runtime unobserved
  6. Diagnosis did not require excessive value logging

    Statement identifier, field types, scope and outcome can suffice for review. Logging every input value does not strengthen permission or query-correctness evidence and may create unnecessary disclosure.

    Intent/query observation
    Bounded decision metadata
    Factory/result comparison
    No-match vs missing retained
    Evidence conclusion
    No excessive value log

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Keep the query factory small and explicit

The public catalog factory validates a closed field set, scalar title, and sort enum. Admitted id/title choices map only to two fixed SELECT statements. Values reach sqlite3 as a tuple; the document branch constructs a fresh application-owned equality filter. Copying a client object is not validation by itself. Type and allowed-field decisions complete before the builder emits its query contract.

Checks should measure behavior beyond restating the source shape: observe native SQLite returning the correct row for an ordinary punctuation title, an unchanged seed row count afterward, and an unknown sort that never increments execute count. For the document matcher, compare wrong-type rejection before lookup with an admitted match returning only projected fields. Driver or framework changes need fresh evaluation; one SQLite result does not generalize to every engine and ODM.

Record failure, scope, and evidence separately

Admissible intent, selected statement/filter, query count, and projected result are distinct fields. Rejection produces a null result; an accepted lookup without a match produces []. Missing intent is NOT-COMPUTED. That small distinction matters during incident review: permission decisions, database connectivity failures, and genuinely absent resources should not become one indistinguishable record. Public visibility is the example’s fixed scope; it does not claim tenant authorization.

A database account should have only the privileges its operation needs; a read-only catalog provides no reason for admin privileges. Production authorization, query timeouts, row/page limits, error disclosure, and transaction policy require additional design. The artifact does not claim to have tested them. Observed native evidence is limited to its own memory database, ordinary seeds, fixed-statement binding, and a narrow literal matcher. Reduced motion preserves the same readable decisions in the interface.

NOW APPLY IT TO THE CASE

When must accepted JSON stop before becoming a query?

The application selects fixed SQL or equality filters from closed intent and a sort enum. Harmless title values remain data; denial, no-match and missing input have distinct tuples.

Compare the cached case representation with its authoritative data owner. Keep stored value, revision, transaction and cache-copy completion separate.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Atlas query contract and public result ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "profile": {
    "allowedKeys": [
      "backend",
      "title",
      "sort"
    ],
    "backends": [
      "sql",
      "document"
    ],
    "titleCodePoints": [
      1,
      40
    ],
    "sortChoices": [
      "id",
      "title"
    ],
    "scope": "public",
    "projection": [
      "id",
      "title"
    ]
  },
  "seed": [
    {
      "id": 1,
      "title": "Tea",
      "visibility": "public"
    },
    {
      "id": 2,
      "title": "O'Reilly",
      "visibility": "public"
    },
    {
      "id": 3,
      "title": "Tea",
      "visibility": "public"
    }
  ],
  "statements": {
    "by-id": "SELECT id, title FROM books WHERE title = ? AND visibility = ? ORDER BY id",
    "by-title": "SELECT id, title FROM books WHERE title = ? AND visibility = ? ORDER BY title, id"
  },
  "inputs": {
    "normal": {
      "backend": "sql",
      "title": "O'Reilly",
      "sort": "id"
    },
    "document": {
      "backend": "document",
      "title": "Tea",
      "sort": "id"
    },
    "wrong-type": {
      "backend": "document",
      "title": {
        "display": "Tea"
      },
      "sort": "id"
    },
    "unknown-sort": {
      "backend": "sql",
      "title": "Tea",
      "sort": "rating"
    }
  },
  "expected": {
    "normal": {
      "decision": "ACCEPT",
      "reason": "public-exact-match",
      "queries": 1,
      "statementId": "by-id",
      "filter": null,
      "rows": [
        {
          "id": 2,
          "title": "O'Reilly"
        }
      ]
    },
    "document": {
      "decision": "ACCEPT",
      "reason": "public-exact-match",
      "queries": 1,
      "statementId": null,
      "filter": {
        "title": "Tea",
        "visibility": "public"
      },
      "rows": [
        {
          "id": 1,
          "title": "Tea"
        },
        {
          "id": 3,
          "title": "Tea"
        }
      ]
    },
    "wrong-type": {
      "decision": "REJECT",
      "reason": "field-type",
      "queries": 0,
      "statementId": null,
      "filter": null,
      "rows": null
    },
    "unknown-sort": {
      "decision": "REJECT",
      "reason": "unknown-sort",
      "queries": 0,
      "statementId": null,
      "filter": null,
      "rows": null
    }
  },
  "missing": {
    "input": null,
    "decision": "NOT-COMPUTED",
    "queries": 0,
    "rows": null
  },
  "acceptedNoMatch": {
    "title": "Bread",
    "decision": "ACCEPT",
    "queries": 1,
    "rows": []
  }
}
inputs
Ordinary typed intent; not client-authored query syntax.
statements / filter
Application-owned structure and fixed public scope.
queries
Completed lookup count; zero on rejection.
rows
Completed projection: no-result null differs from accepted-no-match [].

Own Python SQLite :memory: bound SELECT and a local Python literal-document matcher. No MongoDB server/driver/cursor, network, ORM, arbitrary query, exploit, or user authorization executes.

Two reality checks

Every bound value can select a column or sort.

Value slots do not choose structure; sort/identifiers require approved application mappings.

A query using a JSON object is automatically safe.

Field/type contracts and an application-owned filter are required; parsing is not authorization.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

For the document backend, title is an object {display: Tea}, not a string. Does a query run?

  1. No. field-type with queries=0 and filter=null.
  2. Yes. Every JSON object automatically becomes a database filter.
Read the explained answer

No. field-type with queries=0 and filter=null. — This contract requires a title string. The input object is not directly used as a query operator or filter.

Why does the O'Reilly title remain match data rather than SQL statement source?

Selected lesson artifact fields; teaching example

inputs.normal
{"backend":"sql","title":"O'Reilly","sort":"id"}
statements.by-id
SELECT id, title FROM books WHERE title = ? AND visibility = ? ORDER BY id
expected.normal
{"decision":"ACCEPT","reason":"public-exact-match","queries":1,"statementId":"by-id","filter":null,"rows":[{"id":2,"title":"O'Reilly"}]}
  1. The apostrophe in the title creates new statement structure.
  2. The statement is fixed; title is bound as data to a placeholder.
Read the explained answer

The statement is fixed; title is bound as data to a placeholder. — The by-id statement receives title and visibility through ? placeholders. The result is public id=2; the title does not choose query grammar.

sort=rating has queries=0, while acceptedNoMatch has queries=1 and rows=[]. Why are these different, and is query/data separation authorization?

Read the explained answer

Unknown sort is outside the id/title choices, so it is rejected before querying and rows stays null. Bread is accepted, queried, and returns rows=[] because no record matches. The server selects fixed query structure; parameterization does not replace object/tenant permission. This profile uses a public projection, and document-adapter results are not a real MongoDB deployment test.

□ I separated rejection from a successful no-match result.

□ I did not treat parameterization as resource authorization.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope