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'sq:action(the POST ran it without a session). Three causes, all fixed:q:redirectoutside 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); andnot session.authenticatedwas 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, withcsrf=andrate_limit=, which were not enforced either (AUTH-7).security.max_content_lengthnever reached Flask, so request bodies — uploads — had no size limit. Over the limit (16 MB by default) the answer is413(CFG-2).
Fixed
A
q:ifinside aq:loopinside aq:actiondid not see the loop variable, and a loop'sfrom/to/itemswere read from the wrong context (ACT-8).Keys in
security:orperformance: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 andround(0.125, 2)is 0.13; it was Python's banker's rounding over binary floats (2 and 0.12).ceil()andfloor()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
databasethat does not exist is an error that points toquantum 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:actionabout 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:elsebranch 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 migrateruns 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/blogworks 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/loginnow creates the admin account; there is no default password.projects/quantum-dashboardworks end to end and is tested in CI: create (validated), finish, reopen and delete tasks withq:action, filter by status. It handled POSTs with aq:ifat 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 astartup.pythat patched the framework at run time with imports from the oldsrc/layout. Those deployment files are gone.
Breaking
- In a
condition, a key a scope does not have (session.x,query.x…) isNone(EXPR-8):not session.xis now true whenxis 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:redirectoutside an action now redirects.q:actionrefusesrequire_auth,csrfandrate_limit(AUTH-7).- A datasource that is not in
quantum.config.yamlis an error. The engine used to ask the old admin API onlocalhost:8000for 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 theq:prefix are a parse error (PARSE-1); they were read as the HTML element.