Validate first-party parser boundaries
This commit is contained in:
1 parent
c5a92282a9
commit
6599d0a244
4 files changed
+95
-3
No files matched your search
@@ -2,9 +2,11 @@
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const httpModule = require('node:http');
|
||||
const { createServer } = require('../plugins/worker');
|
||||
const { parse } = require('../plugins/registry');
|
||||
const { parseSource } = require('../plugins/client');
|
||||
const { validate } = require('../plugins/validation');
|
||||
const { discoverWeebCatalog, discoverWeebChapter } = require('../adapters/weebcentral');
|
||||
|
||||
const seriesUrl = 'https://weebcentral.com/series/ABC/title';
|
||||
@@ -21,6 +23,15 @@ test('first-party registry rejects unknown plugins and malformed input', () => {
|
||||
assert.equal(parse('weebcentral', 'catalog', { html: listHtml, url: seriesUrl }).chapters.length, 2);
|
||||
});
|
||||
|
||||
test('parser output cannot turn catalog and image links into local or unrelated requests', () => {
|
||||
assert.throws(() => validate('weebcentral', 'series', { title: 'X', listUrl: 'http://127.0.0.1:8091/api/state' }), /unexpected/);
|
||||
assert.throws(() => validate('weebcentral', 'catalog', { chapterLinks: 1, chapters: [{ id: 'X', number: '1', url: 'https://other.example/chapters/X' }] }), /unexpected/);
|
||||
assert.throws(() => validate('weebcentral', 'chapter', { manifestUrl: 'https://weebcentral.com/chapters/X/not-images' }), /manifest/);
|
||||
assert.throws(() => validate('weebcentral', 'pages', { pages: ['http://127.0.0.1:8080/private'] }), /unsafe/);
|
||||
assert.throws(() => validate('weebcentral', 'pages', { pages: ['https://localhost/private'] }), /unsafe/);
|
||||
assert.doesNotThrow(() => validate('weebcentral', 'pages', { pages: ['https://img.example/1.jpg'] }));
|
||||
});
|
||||
|
||||
test('isolated parser preserves WeebCentral catalog and page discovery', async () => {
|
||||
const server = createServer();
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
@@ -65,3 +76,20 @@ test('configured worker failure does not fall back to local parsing', async () =
|
||||
else process.env.SOURCE_PLUGIN_URL = previous;
|
||||
}
|
||||
});
|
||||
|
||||
test('scraper rejects unsafe responses from a configured worker', async () => {
|
||||
const server = httpModule.createServer((_request, response) => {
|
||||
response.writeHead(200, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ title: 'X', listUrl: 'http://127.0.0.1:8091/api/state' }));
|
||||
});
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
const previous = process.env.SOURCE_PLUGIN_URL;
|
||||
process.env.SOURCE_PLUGIN_URL = `http://127.0.0.1:${server.address().port}`;
|
||||
try {
|
||||
await assert.rejects(parseSource('weebcentral', 'series', { html: seriesHtml, url: seriesUrl }), /unexpected/);
|
||||
} finally {
|
||||
if (previous === undefined) delete process.env.SOURCE_PLUGIN_URL;
|
||||
else process.env.SOURCE_PLUGIN_URL = previous;
|
||||
await new Promise(resolve => server.close(resolve));
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user