...
 
Commits (6)
{
"presets": ["es2015", "stage-0"]
}
......@@ -36,7 +36,7 @@ export const list = {
export const attachments = [
{
filename: '%%CODE%%.css',
content: '* { background-image: url(\'%%BASE%%css_attachment\'); }',
content: '* { background-image: url(\'%%BASE%%cssAttachment\'); }',
cid: '%%CODE%%.css@%%FRONTEND_DOMAIN%%',
contentDisposition: 'inline',
contentType: 'text/css',
......
......@@ -10,7 +10,7 @@ m. Please contact me using the form at https://grepular.com/contact about this.'
} catch(e){}
}
</script>
<text x="0" y="0" style="background-image:url('%%BASE%%svg_background_img')"/>
<text x="0" y="0" style="background-image:url('%%BASE%%svgBackgroundImg')"/>
</svg>`;
export default svg;
......@@ -2,5 +2,5 @@ import svg from './svg';
export default svg
.replace(/(<!DOCTYPE [^>]+)/, '$1 [ <!ELEMENT svg ANY')
.replace(/(<svg )/, '\n<!ENTITY xxe SYSTEM "%%BASE%%svg_xxe" >]>\n$1')
.replace(/(<svg )/, '\n<!ENTITY xxe SYSTEM "%%BASE%%svgXxe" >]>\n$1')
.replace(/(<text[^>]+)\/>/, '$1>&xxe;</text>');
......@@ -22,12 +22,14 @@ export default async function callbackRoute (req, res) {
await test.save();
}
const ip = req.remote();
const ip = AllTests[testName].type === 'dns'
? req.query.ip
: req.remote().client;
const callback = new Callbacks({
code,
ip,
test: testName,
ip: ip.client,
http: {
httpUserAgent: req.headers['user-agent'],
httpForwardedFor: ip.httpForwardedFor,
......@@ -37,12 +39,12 @@ export default async function callbackRoute (req, res) {
newCallback(code);
return res.end(`Successfully recorded ${testName} test`);
return res.json({ ok: `Successfully recorded ${testName} test` });
} catch (err) {
console.log(`Error recording "${testName}" test:`, err.message); // eslint-disable-line no-console
res.end(`Failed to record ${testName} test`);
res.json({ error: `Failed to record ${testName} test` });
}
......
#!/usr/bin/env babel-node
import os from 'os';
import { Tail } from 'tail';
import { frontendDomain } from './frontend/config';
import API from './frontend/lib/api';
const queryLogPath = '/var/log/bind/query.log';
const rx = new RegExp(
`^\\S+ \\S+ .*: client (\\S+?)#\\d+.*?: query: ([a-zA-Z0-9]+)\\.(anchor|link)-test\\.ept\\.${frontendDomain.replace(/\./g, '\\.')} IN A(?:AAA)? `,
'i'
);
// Collect up local IPs so we can exclude them from callbacks
const localIPs = (() => {
const ifaces = os.networkInterfaces();
return Object.keys(ifaces).reduce((o, ifname) => {
ifaces[ifname].forEach(iface => {
if (!iface.family.match(/^IPv[46]$/)) return;
o[iface.address.toLowerCase()] = true;
});
return o;
}, {});
})();
const tail = new Tail(queryLogPath);
const cache = {};
tail.on('line', data => {
// Clear expired cache entries
Object.keys(cache).forEach(k => {
if (cache[k] < Date.now() - 30000) delete cache[k];
});
const m = data.toLowerCase().match(rx);
if (!m) return;
const [ ip, code, label ] = m.slice(1);
const test = 'dns' + label.substr(0,1).toUpperCase() + label.substr(1);
if (localIPs[ip.toLowerCase()]) return;
const cacheKey = [ ip, code, test ].join(' ');
if ({}.hasOwnProperty.call(cache, cacheKey)) return;
cache[cacheKey] = Date.now();
API.get('callback', { ip, code, test })
.then(() => console.log(`Successfully logged ${test} / ${code} / ${ip}`))
.catch(e => console.log(`Failed to log ${test} / ${code} / ${ip} - ${e}`));
});
export const backendUrl = 'http://localhost:3333';
export const frontendUrl = 'http://localhost:3000';
export const backendUrl = 'http://localhost:3333';
export const frontendUrl = 'http://localhost:3000';
export const frontendDomain = 'localhost';
......@@ -111,11 +111,17 @@ export default class TestResultsLabel extends Component {
const { results } = this.props;
let age = null;
let ageSecs = 0;
if (results.length) {
let { time } = results[results.length - 1];
const serverDate = new ServerDate();
if (serverDate.now() < time) time = serverDate.now();
age = moment(time).from(serverDate.date());
const dateNow = serverDate.date();
ageSecs = parseInt((dateNow.getTime() - time) / 1000, 10);
age = ageSecs < 1 ? 'Just now'
: ageSecs === 1 ? '1 second ago'
: ageSecs < 60 ? `${ageSecs} seconds ago`
: moment(time).from(dateNow);
}
clearTimeout(this.refreshAgePid);
......@@ -126,7 +132,7 @@ export default class TestResultsLabel extends Component {
}
if (results.length) {
this.refreshAgePid = setTimeout(this.refreshAge, 10000);
this.refreshAgePid = setTimeout(this.refreshAge, ageSecs < 60 ? 1000 : 10000);
}
}
......
......@@ -122,7 +122,7 @@ export default class Wrapper extends Component {
<a className={ style.header.nav.link } href="/privacy">Privacy</a>
</Link>
<Link href="/donate">
<a className={ style.header.nav.link } href="/donate">Give&#160;Back</a>
<a className={ style.header.nav.link } href="/donate">Donate</a>
</Link>
<a className={ style.header.nav.link } href="https://grepular.com">Contact</a>
</nav>
......
......@@ -93,17 +93,17 @@ export default class TestPage extends Component {
<Wrapper title={ `Testing ${email}` }>
<div className={ style.actions.root }>
{
testSent ? null : (
<button
className = { style.actions.action }
onClick = { this.send }
disabled = { submitting }
>
{
submitting ? 'Sending...' : 'Send Test Email'
}
</button>
)
<button
className = { style.actions.action }
onClick = { this.send }
disabled = { submitting }
>
{
submitting ? 'Sending...'
: testSent ? 'Send Another Email'
: 'Send Test Email'
}
</button>
}
{
results.length ? (
......
......@@ -6,6 +6,7 @@
"scripts": {
"frontend": "next frontend",
"backend": "babel-node backend",
"dns-watcher": "babel-node dns-watcher.js",
"postinstall": "yarn global add babel-cli@6.18.0 next@1.2.3"
},
"devDependencies": {
......@@ -17,6 +18,7 @@
},
"dependencies": {
"ept-backend": "file:backend",
"ept-frontend": "file:frontend"
"ept-frontend": "file:frontend",
"tail": "^1.2.1"
}
}
......@@ -1311,8 +1311,13 @@ enhanced-resolve@~0.9.0:
version "1.0.0"
dependencies:
babel-preset-es2015 "^6.18.0"
babel-preset-react "^6.16.0"
babel-preset-stage-0 "^6.16.0"
body-parser "^1.15.2"
express "^4.14.0"
mongoose "^4.7.2"
nodemailer "^2.7.0"
react-dom "^15.4.1"
"ept-frontend@file:frontend":
version "1.0.0"
......@@ -3474,6 +3479,10 @@ table@^3.7.8:
slice-ansi "0.0.4"
string-width "^2.0.0"
tail@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/tail/-/tail-1.2.1.tgz#c998a0cd9f8bf6dce780a6bc7339e66371e0db3f"
tapable@^0.1.8, tapable@~0.1.8:
version "0.1.10"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
......