mirror of
https://github.com/trufflesecurity/xsshunter
synced 2024-11-23 21:03:11 +00:00
Send emails with sendgrid (#14)
* Use sendgrid to send emails * add unsubscribe list * fix test code * update enabled var and readme * email template --------- Co-authored-by: counter <counter@counters-MacBook-Air.local>
This commit is contained in:
parent
38bebeda34
commit
b1b48104e1
7 changed files with 3236 additions and 60 deletions
12
README.md
12
README.md
|
@ -21,14 +21,10 @@ The following are some YAML fields (in [`docker-compose.yaml`](https://github.co
|
|||
|
||||
The following are needed if you want email notifications:
|
||||
|
||||
* `SMTP_EMAIL_NOTIFICATIONS_ENABLED`: Leave enabled to receive email notifications (you must set this up via the below configurations as well).
|
||||
* `SMTP_HOST`: The host of your SMTP server where your email account is hosted (e.g. `smtp.gmail.com`).
|
||||
* `SMTP_PORT`: The port of your SMTP server (e.g. `465`).
|
||||
* `SMTP_USE_TLS`: Utilize TLS if your SMTP server supports it.
|
||||
* `SMTP_USERNAME`: The username of the email account on your SMTP server (e.g. `exampleuser`).
|
||||
* `SMTP_PASSWORD`: The password of the email account on your SMTP server (e.g. `Password1!`).
|
||||
* `SMTP_FROM_EMAIL`: The email address of your email account on the SMTP server (e.g. `exampleuser@gmail.com`).
|
||||
* `SMTP_RECEIVER_EMAIL`: What email the notifications will be sent to. This may be the same as the above but could be different.
|
||||
* `EMAIL_NOTIFICATIONS_ENABLED`: Leave enabled to receive email notifications (you must set this up via the below configurations as well).
|
||||
* `SENDGRID_API_KEY`: API key for Sendgrid
|
||||
* `SENDGRID_UNSUBSRIBE_GROUP_ID`: Unsubscribe group ID for emails
|
||||
|
||||
|
||||
Finally, the following is worth considering for the security conscious:
|
||||
|
||||
|
|
3
app.js
3
app.js
|
@ -300,8 +300,9 @@ async function get_app_server() {
|
|||
|
||||
console.log("saved record");
|
||||
// Send out notification via configured notification channel
|
||||
if(user.sendEmailAlerts && process.env.SMTP_EMAIL_NOTIFICATIONS_ENABLED=="true") {
|
||||
if(user.sendEmailAlerts && process.env.EMAIL_NOTIFICATIONS_ENABLED=="true") {
|
||||
payload_fire_data.screenshot_url = `https://${process.env.HOSTNAME}/screenshots/${payload_fire_data.screenshot_id}.png`;
|
||||
payload_fire_data.xsshunter_url = `https://${process.env.HOSTNAME}`;
|
||||
await notification.send_email_notification(payload_fire_data, user.email);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const nodemailer = require('nodemailer');
|
||||
const sendgrid = require('@sendgrid/mail')
|
||||
sendgrid.setApiKey(process.env.SENDGRID_API_KEY)
|
||||
const mustache = require('mustache');
|
||||
const fs = require('fs');
|
||||
|
||||
|
@ -8,30 +9,32 @@ const XSS_PAYLOAD_FIRE_EMAIL_TEMPLATE = fs.readFileSync(
|
|||
);
|
||||
|
||||
async function send_email_notification(xss_payload_fire_data, email) {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT),
|
||||
secure: (process.env.SMTP_USE_TLS === "true"),
|
||||
auth: {
|
||||
user: process.env.SMTP_USERNAME,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
const notification_html_email_body = mustache.render(
|
||||
XSS_PAYLOAD_FIRE_EMAIL_TEMPLATE,
|
||||
xss_payload_fire_data
|
||||
);
|
||||
|
||||
const info = await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM_EMAIL,
|
||||
const msg = {
|
||||
from: process.env.EMAIL_FROM,
|
||||
to: email,
|
||||
subject: `[XSS Hunter Express] XSS Payload Fired On ${xss_payload_fire_data.url}`,
|
||||
text: "Only HTML reports are available, please use an email client which supports this.",
|
||||
html: notification_html_email_body,
|
||||
});
|
||||
asm: {
|
||||
groupId: parseInt(process.env.SENDGRID_UNSUBSRIBE_GROUP_ID),
|
||||
groupsToDisplay: [
|
||||
parseInt(process.env.SENDGRID_UNSUBSRIBE_GROUP_ID)
|
||||
]
|
||||
},
|
||||
}
|
||||
response = await sendgrid
|
||||
.send(msg)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
})
|
||||
|
||||
console.log("Message sent: %s", info.messageId);
|
||||
console.log("Message emailed with status %d", response[0].statusCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports.send_email_notification = send_email_notification;
|
||||
|
|
24
notification.test.js
Normal file
24
notification.test.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
const notification = require('./notification');
|
||||
|
||||
test('send an email notification', async () => {
|
||||
const email = "dustin@trufflesec.com"
|
||||
|
||||
var payload_fire_data = {
|
||||
id: 1,
|
||||
user_id: 2,
|
||||
url: "http://google.com",
|
||||
ip_address: "127.0.0.1",
|
||||
referer: "http://google.com",
|
||||
user_agent: "TruffleHog",
|
||||
cookies: [],
|
||||
title: "Hello",
|
||||
secrets: {},
|
||||
origin: "http://google.com",
|
||||
screenshot_id: 1,
|
||||
was_iframe: true,
|
||||
browser_timestamp: 1,
|
||||
correlated_request: 'No correlated request found for this injection.',
|
||||
}
|
||||
|
||||
await expect(notification.send_email_notification(payload_fire_data, email)).resolves.toBe(true);
|
||||
});
|
3186
package-lock.json
generated
3186
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,22 +4,24 @@
|
|||
"description": "Simplified and easy-to-setup version of XSS Hunter for self-hosting.",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"test": "jest",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "mandatory (Matthew Bryant)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deveodk/vue-toastr": "^1.1.0",
|
||||
"@google-cloud/storage": "^6.9.0",
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"@truffledustin/node-client-sessions": "^0.8.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"body-parser": "^1.20.1",
|
||||
"cors": "^2.8.5",
|
||||
"@google-cloud/storage": "^6.9.0",
|
||||
"express": "^4.17.1",
|
||||
"express-jsonschema": "^1.1.6",
|
||||
"google-auth-library": "^8.7.0",
|
||||
"googleapis": "^110.0.0",
|
||||
"jest": "^29.4.1",
|
||||
"keygrip": "^1.1.0",
|
||||
"memorystore": "^1.6.6",
|
||||
"moment": "^2.29.1",
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
</head>
|
||||
|
||||
<body style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin: 0;font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;font-size: 14px;line-height: 1.42857143;color: #333;background-color: #fff;">
|
||||
<h1 style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin: .67em 0;font-size: 36px;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 20px;margin-bottom: 10px;">XSS Hunter Express Report</h1>
|
||||
This report has been generated by an XSS Hunter Express server and contains the details of a cross-site scripting vulnerability. The tracking ID for this report is <code style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;font-size: 90%;padding: 2px 4px;color: #c7254e;background-color: #f9f2f4;border-radius: 4px;">{{ id }}</code>, the triggering browser reports the time of execution to be {{ browser_timestamp }}.
|
||||
<h1 style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin: .67em 0;font-size: 36px;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 20px;margin-bottom: 10px;">XSSHunter Report</h1>
|
||||
This report has been generated by an XSSHunter server and contains the details of a cross-site scripting vulnerability. To view more details including vulnerability checks for secrets, CORS, and .git exposed, login here: <a href="{{xsshunter_url}}" the triggering browser reports the time of execution to be {{ browser_timestamp }}.
|
||||
<hr style="-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;height: 0;margin-top: 20px;margin-bottom: 20px;border: 0;border-top: 1px solid #eee;">
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
|
@ -43,14 +43,6 @@
|
|||
<code style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;font-size: 90%;padding: 2px 4px;color: #c7254e;background-color: #f9f2f4;border-radius: 4px;">{{ user_agent }}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
<h3 class="panel-title" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 0;margin-bottom: 0;font-size: 16px;">Cookies</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 15px;">
|
||||
<code style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;font-size: 90%;padding: 2px 4px;color: #c7254e;background-color: #f9f2f4;border-radius: 4px;">{{ cookies }}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
<h3 class="panel-title" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 0;margin-bottom: 0;font-size: 16px;">Injection Point (Raw HTTP Request)</h3>
|
||||
|
@ -59,22 +51,6 @@
|
|||
<pre class="pre-scrollable" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;overflow: auto;display: block;padding: 9.5px;margin: 0 0 10px;font-size: 1em;line-height: 1.42857143;color: #333;word-break: break-all;word-wrap: break-word;background-color: #f5f5f5;border: 1px solid #999;border-radius: 4px;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;page-break-inside: avoid;max-height: 340px;overflow-y: scroll;">{{correlated_request}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
<h3 class="panel-title" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 0;margin-bottom: 0;font-size: 16px;">Page DOM</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 15px;">
|
||||
<pre class="pre-scrollable" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;overflow: auto;display: block;padding: 9.5px;margin: 0 0 10px;font-size: 1em;line-height: 1.42857143;color: #333;word-break: break-all;word-wrap: break-word;background-color: #f5f5f5;border: 1px solid #999;border-radius: 4px;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;page-break-inside: avoid;max-height: 340px;overflow-y: scroll;">{{ dom }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
<h3 class="panel-title" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 0;margin-bottom: 0;font-size: 16px;">Page Text</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 15px;">
|
||||
<pre class="pre-scrollable" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;overflow: auto;display: block;padding: 9.5px;margin: 0 0 10px;font-size: 1em;line-height: 1.42857143;color: #333;word-break: break-all;word-wrap: break-word;background-color: #f5f5f5;border: 1px solid #999;border-radius: 4px;font-family: Menlo,Monaco,Consolas,"Courier New",monospace;page-break-inside: avoid;max-height: 340px;overflow-y: scroll;">{{ text }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;margin-bottom: 20px;background-color: #fff;border: 1px solid transparent;border-radius: 4px;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);box-shadow: 0 1px 2px rgba(0,0,0,.05);border-color: #ddd;">
|
||||
<div class="panel-heading" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 10px 15px;border-bottom: 1px solid transparent;border-top-left-radius: 3px;border-top-right-radius: 3px;color: #333;background-color: #f5f5f5;border-color: #ddd;background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat: repeat-x;">
|
||||
<h3 class="panel-title" style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 0;margin-bottom: 0;font-size: 16px;">Execution Origin</h3>
|
||||
|
@ -86,6 +62,8 @@
|
|||
<h3 style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;orphans: 3;widows: 3;page-break-after: avoid;font-family: inherit;font-weight: 500;line-height: 1.1;color: inherit;margin-top: 20px;margin-bottom: 10px;font-size: 24px;"><i style="-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;">A screenshot of the affected page has been included for further investigation.</i></h3>
|
||||
<hr style="-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;height: 0;margin-top: 20px;margin-bottom: 20px;border: 0;border-top: 1px solid #eee;">
|
||||
<img alt="Enable images to see the XSS screenshot" src="{{ screenshot_url }}" />
|
||||
<br>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
|
|
Loading…
Reference in a new issue