Skip to content

Quantum 0.13.0 ​

Security ​

  • A guard written by hand protected nothing. <q:if condition="not session.authenticated"><q:redirect url="/login"/></q:if> at the top of a page did not redirect (the GET served the page) and did not stop the page's q:action (the POST ran it without a session). Three causes, all fixed: q:redirect outside an action was skipped silently (ACT-7); an action never ran the page's statements, so no check at the top could stop it — guards now run before each action (AUTH-6); and not session.authenticated was false for a visitor with no session, because a missing key made the whole condition false (EXPR-8).
  • <q:action require_auth="true"> was parsed and never checked: the action ran for anyone. It is now a parse error, with csrf= and rate_limit=, which were not enforced either (AUTH-7).
  • security.max_content_length never reached Flask, so request bodies — uploads — had no size limit. Over the limit (16 MB by default) the answer is 413 (CFG-2).

Fixed ​

  • A q:if inside a q:loop inside a q:action did not see the loop variable, and a loop's from/to/items were read from the wrong context (ACT-8).

  • Keys in security: or performance: that nothing implements (csrf_protection, rate_limiting, cors_*…) are named in a warning when the config loads (CFG-2).

  • round() rounds halves away from zero — round(2.5) is 3 and round(0.125, 2) is 0.13; it was Python's banker's rounding over binary floats (2 and 0.12). ceil() and floor() are new (EXPR-9).

  • A JavaScript habit in an expression (Math.ceil, Date.now(), text.split(' '), parseInt) names the function to use instead (EXPR-10).

  • A SQLite database that does not exist is an error that points to quantum migrate up; it used to be created empty, so the page failed later with "no such table" (DB-7). A datasource key nothing reads (sqlite_path, pool_size) is named in a warning.

  • An error inside a q:action about a missing variable says why: an action does not run the page's statements (ACT-9).

  • Error pages no longer suggest quantum inspect, which does not exist.

  • Text written directly in a q:if / q:else branch is rendered (IF-3): <h2><q:if condition="tag">Posts tagged {tag}</q:if><q:else>All</q:else></h2> rendered an empty heading.

  • quantum migrate runs migration files with more than one statement, and applies each file in one transaction: SQLite accepted one statement per file ("You can only execute one statement at a time"), so a table and its index could not be in the same migration (DB-8).

  • slugify(text) makes a URL segment from a title (EXPR-9).

  • projects/blog works end to end and is tested in CI (tests/apps): home with tag filter and pages, posts with comments, search, and an admin to create, edit, publish and delete posts. It had failed on every page. Its SQL was PostgreSQL-only while its database was SQLite, it seeded an admin whose password nobody knew, and it protected the admin with a hand-written guard that did not protect it. The first visit to /login now creates the admin account; there is no default password.

  • projects/quantum-dashboard works end to end and is tested in CI: create (validated), finish, reopen and delete tasks with q:action, filter by status. It handled POSTs with a q:if at the top of the page, so there was no validation and reloading repeated the operation; its database lived in /app/data; and it started through a startup.py that patched the framework at run time with imports from the old src/ layout. Those deployment files are gone.

Breaking ​

  • In a condition, a key a scope does not have (session.x, query.x…) is None (EXPR-8): not session.x is now true when x is missing.
  • A guard that reads a variable the page sets, on a page with actions, is a parse error; a guard condition that cannot be evaluated is an error instead of false (AUTH-6).
  • q:redirect outside an action now redirects.
  • q:action refuses require_auth, csrf and rate_limit (AUTH-7).
  • A datasource that is not in quantum.config.yaml is an error. The engine used to ask the old admin API on localhost:8000 for it (DB-7).
  • Session, application and request values are read only with their prefix — session.role, request.path. They were also copied in as bare names, so {role} or {path} silently read them (EXPR-11).
  • <q:html>, <q:div> and other HTML element names with the q: prefix are a parse error (PARSE-1); they were read as the HTML element.

← 0.12.0 · All versions · 0.14.0 →

MIT Licensed · Built with VitePress