mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 01:20:07 -05:00
Mark the guarded publication arguments as checked, or the audit throws.
The guard replaced `check(boardId, String)` with `Match.test(...)`, and every subscription then failed with Exception from sub board id ... Error: Did not check() all arguments during publisher 'board' This app runs with audit-argument-checks: a publisher that returns without having check()ed every one of its arguments is failed by the audit, and `Match.test` is not checking - it only asks. So the server no longer died, but no board could be subscribed to either. Each guarded publisher now marks its arguments with `check(x, Match.Any)` - which satisfies the audit and cannot throw - and validates them with `Match.test` immediately after. `board`, `boardCardsWindow` and `boardCardsLoadingMode` all do this. Checked the other publications too: `activities` was the only other one that looked unchecked, and it does check its argument, over several lines. tests/publicationArgumentGuard.test.cjs now requires both halves - every argument marked with Match.Any, and the marking before the validation - so removing the check() again fails the suite instead of the server. Thanks to xet7 !
This commit is contained in:
1 parent
66be7313f3
commit
b3537985eb
3 files changed
+32
No files matched your search
@@ -489,6 +489,15 @@ publishComposite('board', async function(boardId, isArchived) {
|
||||
// Publishing nothing is the right answer to a subscription that names no
|
||||
// board: publishComposite treats a falsy return as "no publications" and
|
||||
// readies the subscription, so the client simply gets an empty result.
|
||||
//
|
||||
// `check(..., Match.Any)` first: this app runs with audit-argument-checks, which
|
||||
// fails a publisher that returns without having check()ed every argument -
|
||||
// "Did not check() all arguments during publisher 'board'". Match.test alone
|
||||
// does not count as checking, so the audit threw for every subscription. Any
|
||||
// marks them audited and never throws; the real validation is the two lines
|
||||
// below it.
|
||||
check(boardId, Match.Any);
|
||||
check(isArchived, Match.Any);
|
||||
if (!Match.test(boardId, String) || !boardId) return;
|
||||
if (!Match.test(isArchived, Boolean)) return;
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ async function boardVisibleTo(userId, boardId) {
|
||||
publishComposite('boardCardsWindow', function(boardId, cardSelector, sort, limit) {
|
||||
// Same as the `board` publication: a bad argument from a client must end the
|
||||
// subscription, not the server process. A falsy return publishes nothing.
|
||||
// `Match.Any` marks each argument as checked for audit-argument-checks (which
|
||||
// fails a publisher that did not check them all); the validation is below.
|
||||
check(boardId, Match.Any);
|
||||
check(cardSelector, Match.Any);
|
||||
check(sort, Match.Any);
|
||||
check(limit, Match.Any);
|
||||
if (!Match.test(boardId, String) || !boardId) return;
|
||||
if (!Match.test(cardSelector, Object)) return;
|
||||
if (!Match.test(sort, Match.OneOf(Object, null, undefined))) return;
|
||||
@@ -184,6 +190,8 @@ Meteor.publish('boardListCardCount', async function(countId, boardId, cardSelect
|
||||
// is rare and picked up on the next board open). #6480.
|
||||
Meteor.publish('boardCardsLoadingMode', async function(boardId) {
|
||||
// A null board id here is the same crash: this publisher is async too.
|
||||
// Match.Any marks it checked for audit-argument-checks; the test is the guard.
|
||||
check(boardId, Match.Any);
|
||||
if (!Match.test(boardId, String) || !boardId) return this.ready();
|
||||
|
||||
const board = await boardVisibleTo(this.userId, boardId);
|
||||
|
||||
@@ -61,6 +61,16 @@ test('the board publisher tests its arguments before anything else', () => {
|
||||
assert.ok(/if \(!Match\.test\(isArchived, Boolean\)\) return;/.test(head));
|
||||
assert.ok(!/^\s*check\(boardId, String\);/m.test(head),
|
||||
'check() throws, and a throw here exits the process - it must not be the guard');
|
||||
// ...but the arguments must still be MARKED as checked: this app runs with
|
||||
// audit-argument-checks, which fails any publisher that returns without having
|
||||
// check()ed every argument ("Did not check() all arguments during publisher
|
||||
// 'board'"). Match.test does not count; check(x, Match.Any) does, and never
|
||||
// throws.
|
||||
assert.ok(/check\(boardId, Match\.Any\);/.test(head)
|
||||
&& /check\(isArchived, Match\.Any\);/.test(head),
|
||||
'every argument must be check()ed with Match.Any, or the audit throws');
|
||||
assert.ok(head.indexOf('check(boardId, Match.Any)') < head.indexOf('Match.test(boardId'),
|
||||
'marked first, validated second');
|
||||
// The guard has to come before any work, or the work throws first.
|
||||
assert.ok(head.indexOf('Match.test(boardId') < head.indexOf('localizeBoardMemberAvatars'),
|
||||
'and it must run before the publisher does anything with the id');
|
||||
@@ -71,7 +81,12 @@ test('the card-window publishers are guarded the same way', () => {
|
||||
assert.ok(/if \(!Match\.test\(boardId, String\) \|\| !boardId\) return;/.test(window.slice(0, 900)));
|
||||
assert.ok(/if \(!Match\.test\(cardSelector, Object\)\) return;/.test(window.slice(0, 900)),
|
||||
'a card selector from a client is an object or nothing at all');
|
||||
for (const arg of ['boardId', 'cardSelector', 'sort', 'limit']) {
|
||||
assert.ok(new RegExp(`check\\(${arg}, Match\\.Any\\);`).test(window.slice(0, 900)),
|
||||
`${arg} must be marked as checked for the audit`);
|
||||
}
|
||||
const mode = cardsWindow.slice(cardsWindow.indexOf("Meteor.publish('boardCardsLoadingMode'"));
|
||||
assert.ok(/check\(boardId, Match\.Any\);/.test(mode.slice(0, 500)));
|
||||
assert.ok(/if \(!Match\.test\(boardId, String\) \|\| !boardId\) return this\.ready\(\);/
|
||||
.test(mode.slice(0, 500)),
|
||||
'a plain publisher readies the subscription instead of returning a config');
|
||||
|
||||
Reference in new issue
Block a user