fix: enforce private feedback access across list and detail
Deploy EG-BIM QA Gateway / deploy (push) Successful in 50s
Deploy EG-BIM QA Gateway / deploy (push) Successful in 50s
This commit is contained in:
1 parent
e2e70c13ac
commit
a5a5f24f92
10 files changed
+379
-24
No files matched your search
@@ -21,15 +21,16 @@ function element() {
|
||||
};
|
||||
}
|
||||
|
||||
async function detailPage({ comments = [], cachedComments = [], createResponse = {}, patchResponse = {}, authRefresh = false, user = USER, postOwner = USER.requesterId } = {}) {
|
||||
async function detailPage({ comments = [], cachedComments = [], cachedPosts = [], createResponse = {}, patchResponse = {}, authRefresh = false, user = USER, postOwner = USER.requesterId, isSecret = false, detailStatus = 200, commentsStatus = 200 } = {}) {
|
||||
const elements = new Map([
|
||||
'#detail', '[data-detail-category]', '[data-detail-title]', '[data-detail-status]',
|
||||
'[data-detail-date]', '[data-detail-author]', '[data-detail-company]',
|
||||
'[data-detail-department]', '[data-detail-content]', '[data-detail-secret]',
|
||||
'#comments', '#commentCount', '#commentForm', '#commentContent',
|
||||
'#commentError', '#commentSubmit', '[data-comment-edit-input]', '#editButton', '#deleteButton'
|
||||
'#commentError', '#commentSubmit', '[data-comment-edit-input]', '#editButton', '#deleteButton',
|
||||
'.comment-box', '[data-detail-attachments]'
|
||||
].map((selector) => [selector, element()]));
|
||||
const storage = new Map([[COMMENTS_KEY, JSON.stringify({ [POST]: cachedComments })]]);
|
||||
const storage = new Map([[COMMENTS_KEY, JSON.stringify({ [POST]: cachedComments })], ['baron.qa.posts.egbim', JSON.stringify(cachedPosts)]]);
|
||||
const requests = [];
|
||||
const context = vm.createContext({
|
||||
window: { QA_CONFIG: {}, location: { origin: 'https://qa.example.test', search: '?id=' + POST }, confirm: () => true },
|
||||
@@ -45,8 +46,8 @@ async function detailPage({ comments = [], cachedComments = [], createResponse =
|
||||
else if (method === 'PATCH') body = patchResponse;
|
||||
else if (method === 'DELETE') body = { ok: true };
|
||||
else if (path.endsWith('/comments')) body = { comments };
|
||||
else body = { id: POST, title: '문의 제목', contents: '문의 내용', requester_id: postOwner, requester_name: USER.name };
|
||||
return new Response(JSON.stringify(body));
|
||||
else body = { id: POST, title: '문의 제목', contents: '문의 내용', requester_id: postOwner, requester_name: USER.name, is_secret: isSecret };
|
||||
return new Response(JSON.stringify(body), { status: method === 'GET' ? path.endsWith('/comments') ? commentsStatus : detailStatus : 200 });
|
||||
}
|
||||
});
|
||||
vm.runInContext(source.replace(/\}\)\(\);\s*$/, `
|
||||
@@ -142,6 +143,36 @@ test('a cached comment without an ID remains read-only when loading the page', a
|
||||
assert.equal(page.requests.some((request) => ['PATCH', 'DELETE'].includes(request.method)), false);
|
||||
});
|
||||
|
||||
for (const status of [401, 403, 404, 500]) {
|
||||
test(`detail HTTP ${status} never falls back to a previous account's saved post or comments`, async () => {
|
||||
const page = await detailPage({ detailStatus: status, cachedPosts: [{ id: POST, title: 'CACHED_TITLE', content: 'CACHED_BODY', secret: true }], cachedComments: [comment({ content: 'CACHED_COMMENT' })] });
|
||||
assert.doesNotMatch(page.elements.get('[data-detail-title]').textContent, /CACHED_/);
|
||||
assert.doesNotMatch(page.elements.get('[data-detail-content]').textContent, /CACHED_/);
|
||||
assert.doesNotMatch(page.html(), /CACHED_/);
|
||||
assert.equal(page.elements.get('#commentForm').hidden, true);
|
||||
assert.equal(page.elements.get('#editButton').hidden, true);
|
||||
assert.equal(page.elements.get('#deleteButton').hidden, true);
|
||||
assert.equal(page.requests.some(request => request.path.endsWith('/comments')), false);
|
||||
});
|
||||
}
|
||||
|
||||
test('a non-owner private detail stays hidden even if the API incorrectly returns the whole post', async () => {
|
||||
const page = await detailPage({ postOwner: 'another-user', isSecret: true, cachedComments: [comment()] });
|
||||
assert.equal(page.elements.get('[data-detail-title]').textContent, '비밀글입니다.');
|
||||
assert.doesNotMatch(page.elements.get('[data-detail-content]').textContent, /문의 내용/);
|
||||
assert.equal(page.elements.get('#commentForm').hidden, true);
|
||||
assert.equal(page.elements.get('[data-detail-attachments]').hidden, true);
|
||||
assert.equal(page.requests.some(request => request.path.endsWith('/comments')), false);
|
||||
});
|
||||
|
||||
test('comment errors never restore cached comments, and own private comments are not persisted', async () => {
|
||||
const denied = await detailPage({ commentsStatus: 403, cachedComments: [comment({ content: 'CACHED_COMMENT' })] });
|
||||
assert.doesNotMatch(denied.html(), /CACHED_COMMENT/);
|
||||
const own = await detailPage({ isSecret: true, comments: [comment()] });
|
||||
assert.match(own.html(), /원래 댓글/);
|
||||
assert.equal(own.stored(), undefined);
|
||||
});
|
||||
|
||||
test('another author post hides both post buttons while the viewer own comment keeps its actions', async () => {
|
||||
const page = await detailPage({ postOwner: 'another-user', comments: [comment(), comment({ id: SECOND, author_id: 'another-user' })] });
|
||||
assert.equal(page.elements.get('#editButton').hidden, true);
|
||||
|
||||
Reference in new issue
Block a user