Skip to content
This repository was archived by the owner on Oct 6, 2022. It is now read-only.

Commit 324ac99

Browse files
a335926asamuelli
authored andcommitted
fix: clean errors, add validation & reduce exposed (#88)
1 parent 16ade32 commit 324ac99

File tree

5 files changed

+58
-41
lines changed

5 files changed

+58
-41
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
node_modules/
1+
node_modules/
2+
yarn.lock

src/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,6 @@ <h1>Rendertron</h1>
206206
}
207207
</script>
208208

209-
<link rel="import" href="node_modules/progress-bar-element/progress-bar.html">
209+
<link rel="import" href="progress-bar.html">
210210
</body>
211211
</html>

src/main.js

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,29 @@
1717
'use strict';
1818

1919
const assert = require('assert');
20-
const renderer = require('./renderer');
21-
const chromeLauncher = require('chrome-launcher');
22-
const express = require('express');
2320
const fs = require('fs');
24-
const compression = require('compression');
25-
const path = require('path');
2621
const https = require('https');
27-
const app = express();
28-
const cache = require('./cache');
22+
const path = require('path');
23+
const url = require('url');
24+
const chromeLauncher = require('chrome-launcher');
25+
const compression = require('compression');
26+
const express = require('express');
2927
const now = require('performance-now');
3028
const uuidv4 = require('uuid/v4');
29+
const cache = require('./cache');
30+
const renderer = require('./renderer');
31+
32+
const app = express();
33+
34+
const CONFIG_PATH = path.resolve(__dirname, '../config.json');
35+
const PROGRESS_BAR_PATH = path.resolve(__dirname, '../node_modules/progress-bar-element/progress-bar.html');
36+
const PORT = process.env.PORT || '3000';
3137

32-
// Load config from config.json if it exists.
3338
let config = {};
34-
const configPath = path.resolve(__dirname, '../config.json');
3539

36-
if (fs.existsSync(configPath)) {
37-
config = JSON.parse(fs.readFileSync(configPath));
40+
// Load config from config.json if it exists.
41+
if (fs.existsSync(CONFIG_PATH)) {
42+
config = JSON.parse(fs.readFileSync(CONFIG_PATH));
3843
assert(config instanceof Object);
3944
}
4045

@@ -55,21 +60,24 @@ app.setConfig = (newConfig) => {
5560
};
5661

5762
app.use(compression());
58-
59-
app.use('/node_modules', express.static(path.resolve(__dirname, '../node_modules')));
63+
app.use('/progress-bar.html', express.static(PROGRESS_BAR_PATH));
6064

6165
app.get('/', (request, response) => {
6266
response.sendFile(path.resolve(__dirname, 'index.html'));
6367
});
6468

65-
function isRestricted(url) {
66-
if (!config['renderOnly'])
67-
return false;
69+
function isRestricted(urlReq) {
70+
const protocol = (url.parse(urlReq).protocol || '');
71+
72+
if (!protocol.match(/^https?/)) return true;
73+
if (!config['renderOnly']) return false;
74+
6875
for (let i = 0; i < config['renderOnly'].length; i++) {
69-
if (url.startsWith(config['renderOnly'][i])) {
76+
if (urlReq.startsWith(config['renderOnly'][i])) {
7077
return false;
7178
}
7279
}
80+
7381
return true;
7482
}
7583

@@ -100,10 +108,8 @@ app.get('/render/:url(*)', async(request, response) => {
100108
response.status(result.status).send(result.body);
101109
track('render', now() - start);
102110
} catch (err) {
103-
let message = `Cannot render ${request.params.url}`;
104-
if (err && err.message)
105-
message += ` - "${err.message}"`;
106-
response.status(400).send(message);
111+
response.status(400).send('Cannot render requested URL');
112+
console.error(err);
107113
}
108114
});
109115

@@ -124,19 +130,16 @@ app.get('/screenshot/:url(*)', async(request, response) => {
124130
response.end(img);
125131
track('screenshot', now() - start);
126132
} catch (err) {
127-
let message = `Cannot render ${request.params.url}`;
128-
if (err && err.message)
129-
message += ` - "${err.message}"`;
130-
response.status(400).send(message);
133+
response.status(400).send('Cannot render requested URL');
134+
console.error(err);
131135
}
132136
});
133137

134138
app.get('/_ah/health', (request, response) => response.send('OK'));
135139

136-
app.get('/_ah/stop', async(request, response) => {
140+
app.stop = async() => {
137141
await config.chrome.kill();
138-
response.send('OK');
139-
});
142+
};
140143

141144
const appPromise = chromeLauncher.launch({
142145
chromeFlags: ['--headless', '--disable-gpu', '--remote-debugging-address=0.0.0.0'],
@@ -147,10 +150,9 @@ const appPromise = chromeLauncher.launch({
147150
config.port = chrome.port;
148151
// Don't open a port when running from inside a module (eg. tests). Importing
149152
// module can control this.
150-
const port = process.env.PORT || '3000';
151153
if (!module.parent) {
152-
app.listen(port, function() {
153-
console.log('Listening on port', port);
154+
app.listen(PORT, function() {
155+
console.log('Listening on port', PORT);
154156
});
155157
}
156158
return app;
@@ -162,6 +164,7 @@ const appPromise = chromeLauncher.launch({
162164

163165

164166
let exceptionCount = 0;
167+
165168
async function logUncaughtError(error) {
166169
console.error('Uncaught exception');
167170
console.error(error);
@@ -170,7 +173,7 @@ async function logUncaughtError(error) {
170173
if (exceptionCount > 5) {
171174
console.log(`Detected ${exceptionCount} errors, shutting instance down`);
172175
if (config && config.chrome)
173-
await config.chrome.kill();
176+
await app.stop();
174177
process.exit(1);
175178
}
176179
}

src/renderer.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ class Renderer {
2626
*/
2727
function getStatusCode() {
2828
const metaElement = document.querySelector('meta[name="render:status_code"]');
29-
if (!metaElement)
30-
return undefined;
29+
if (!metaElement) return;
3130
return parseInt(metaElement.getAttribute('content')) || undefined;
3231
}
3332

test/app-test.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,19 @@ const express = require('express');
2323

2424
const app = express();
2525
app.use(express.static(path.resolve(__dirname, 'resources')));
26+
27+
const appInstances = [];
2628
const testBase = 'http://localhost:1234/';
2729

2830
test.before(async(t) => {
2931
await app.listen(1234);
3032
});
3133

34+
test.after.always(async(t) => {
35+
for (let app of appInstances) {
36+
await app.stop();
37+
}
38+
});
3239

3340
/**
3441
* This deletes server from the require cache and reloads
@@ -41,6 +48,7 @@ async function createServer(config) {
4148
const app = await require('../src/main.js');
4249
if (config)
4350
app.setConfig(config);
51+
appInstances.push(app);
4452
return request(app);
4553
}
4654

@@ -113,15 +121,15 @@ test('server status code should be forwarded', async(t) => {
113121

114122
test('http status code should be able to be set via a meta tag', async(t) => {
115123
const server = await createServer();
116-
const testFile = path.resolve(__dirname, 'resources/http-meta-status-code.html');
117-
const res = await server.get('/render/file://' + testFile + '?wc-inject-shadydom=true');
124+
const testFile = 'http-meta-status-code.html';
125+
const res = await server.get(`/render/${testBase}${testFile}?wc-inject-shadydom=true`);
118126
t.is(res.status, 400);
119127
});
120128

121129
test('http status codes need to be respected from top to bottom', async(t) => {
122130
const server = await createServer();
123-
const testFile = path.resolve(__dirname, 'resources/http-meta-status-code-multiple.html');
124-
const res = await server.get('/render/file://' + testFile + '?wc-inject-shadydom=true');
131+
const testFile = 'http-meta-status-code-multiple.html';
132+
const res = await server.get(`/render/${testBase}${testFile}?wc-inject-shadydom=true`);
125133
t.is(res.status, 401);
126134
});
127135

@@ -136,7 +144,7 @@ test('screenshot is an image', async(t) => {
136144
test('invalid url fails', async(t) => {
137145
const server = await createServer();
138146
const res = await server.get(`/render/abc`);
139-
t.is(res.status, 400);
147+
t.is(res.status, 403);
140148
});
141149

142150
test('unknown url fails', async(t) => {
@@ -145,6 +153,12 @@ test('unknown url fails', async(t) => {
145153
t.is(res.status, 400);
146154
});
147155

156+
test('file url fails', async(t) => {
157+
const server = await createServer();
158+
const res = await server.get(`/render/file:///dev/fd/0`);
159+
t.is(res.status, 403);
160+
});
161+
148162
test('explicit render event ends early', async(t) => {
149163
const server = await createServer();
150164
const res = await server.get(`/render/${testBase}explicit-render-event.html`);

0 commit comments

Comments
 (0)