How to white list sub domains in Node js - javascript

I'm using the below regex to white-list the domains, but the below code xxx.sampledomain.com is not working
let url = req.headers.origin.replace(/^(?:https?:\/\/)?(?:www\.)?/i, "").split('/')[0]
let client = CLIENTS.filter(client => client.websiteAddress.replace('www.', '') === url)[0];
Please guide

I am adding a code . It will check. allow to that subdomain or not
// List of allowed subdomains
const allowedSubdomains = ["subdomain1", "subdomain2", "subdomain3"];
const subdomainRegex = new RegExp(`^(?:https?:\/\/)?(?:www\.)?(${allowedSubdomains.join("|")})\.`);
// Function to check if a subdomain is allowed
function isAllowedSubdomain(subdomain) {
return subdomainRegex.test(subdomain);
}
For example:
let subdomain = req.headers.origin.match(subdomainRegex)[1];
// Extract the subdomain from the origin
if (isAllowedSubdomain(subdomain)) {
console.log("Allow this subdomain")
} else {
console.log("This subdomain is not allowed")
}
If you have any question, You can ask

You can use cors, a npm lib that can help you achieve the same functionality.
var express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['http://example1.com', 'http://example2.com']
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a whitelisted domain.'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})

Related

CORS error despite changing Access-Control-Allow-Methods with CORS middleware when deployed in Heroku

I'm running one client local server with "VSC's Live servers" on localhost:5500, and I'm running a remote server using heroku.
Here is the relevant code regarding the AJAX call client-side when submitting a form:
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://git.heroku.com/morning-falls-52888.git");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JContactRequest);
xhr.onload = () => {
const status = xhr.status;
if (status >= 200 && status < 300 ) {
$("form").append("<p id='submit-message' style='text-align:center;'>Thank you for your submission!</p>");
} else if (status == 418 ) {
window.alert(`Error: ${xhr.status}\nCannot brew coffee, I am a teapot.`);
} else {
console.log(`Before append ${status} ready state: ${xhr.readystate}`);
$("form").append("<p id='submit-message' style='text-align:center;'>Submission Failed</p>");
console.log(`After append ${status} ready state: ${xhr.readystate}`);
}
}
And here is the code server side:
require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json());
const PORT = process.env.PORT ||4500;
const cors = require("cors");
app.use(cors());
app.listen(PORT, () => { console.log(`Listening on PORT: ${PORT}`) });
// app.use((req,res,next)=>{
// res.status(404).send('this is working');
// })
app.get('/', (req, res) => {
res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
res.status(201).send();
console.log("received get request");
})
app.post('/', (req, res) => {
var body=req.body;
console.log(body);
for(let [key,value] of Object.entries(body)){
if(value==""){
res.status(400).send(`Empty Field in ${key}`);
res.end();
break;
}
}
var email = body.emailfield;
if (body.comment == "Can you brew coffee?") {
res.status(418).send();
} else {
res.status(200).send('message sent');
}
// calling the api
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: email,
from: 'd#gmail.com', // Use the email address or domain you verified above
templateId:'*************************************',
dynamic_template_data:body,
}
//ES6
sgMail
.send(msg)
.then(() => {}, error => {
console.error(error);
if (error.response) {
console.error(error.response.body)
console.log(process.env.SENDGRID_API_KEY)
}
});
});
The message I'm getting on my console is the following:
Access to XMLHttpRequest at 'https://git.heroku.com/morning-falls-52888.git' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm kind of stumped because I would have thought that app.use(cors()) would have access-control-allow-origin=*.
I'm still very new to this, so any help is mighty appreciated. Thanks!
EDIT I just wanted to add that this code was running fine when I was running the server locally on port 4500
xhr.open("POST", "https://git.heroku.com/morning-falls-52888.git");
You're making an HTTP request to Heroku's Git repo, not to your application running on Heroku (which will have a URL like https://APPLICATION_NAME.herokuapp.com/something)
The CORS permissions have to be granted for the URL you are making the request to, not one that is loosely adjacent to it.

Access from origin 'https://example.com' has been blocked even though I've allowed https://example.com/

I have an app made with React, Node.js and Socket.io
I deployed Node backend to heroku , frontend to Netlify
I know that CORS errors is related to server but no matter what I add, it just cant go through that error in the picture below.
I also added proxy script to React's package.json as "proxy": "https://googledocs-clone-sbayrak.herokuapp.com/"
And here is my server.js file;
const mongoose = require('mongoose');
const Document = require('./Document');
const dotenv = require('dotenv');
const path = require('path');
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
dotenv.config();
const app = express();
app.use(cors());
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: 'https://googledocs-clone-sbayrak.netlify.app/',
methods: ['GET', 'POST'],
},
});
app.get('/', (req, res) => {
res.status(200).send('hello!!');
});
const connectDB = async () => {
try {
const connect = await mongoose.connect(process.env.MONGODB_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log('MongoDB Connected...');
} catch (error) {
console.error(`Error : ${error.message}`);
process.exit(1);
}
};
connectDB();
let defaultValue = '';
const findOrCreateDocument = async (id) => {
if (id === null) return;
const document = await Document.findById({ _id: id });
if (document) return document;
const result = await Document.create({ _id: id, data: defaultValue });
return result;
};
io.on('connection', (socket) => {
socket.on('get-document', async (documentId) => {
const document = await findOrCreateDocument(documentId);
socket.join(documentId);
socket.emit('load-document', document.data);
socket.on('send-changes', (delta) => {
socket.broadcast.to(documentId).emit('receive-changes', delta);
});
socket.on('save-document', async (data) => {
await Document.findByIdAndUpdate(documentId, { data });
});
});
console.log('connected');
});
server.listen(process.env.PORT || 5000, () =>
console.log(`Server has started.`)
);
and this is where I make request from frontend;
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
import { useParams } from 'react-router-dom';
import { io } from 'socket.io-client';
const SAVE_INTERVAL_MS = 2000;
const TextEditor = () => {
const [socket, setSocket] = useState();
const [quill, setQuill] = useState();
const { id: documentId } = useParams();
useEffect(() => {
const s = io('https://googledocs-clone-sbayrak.herokuapp.com/');
setSocket(s);
return () => {
s.disconnect();
};
}, []);
/* below other functions */
/* below other functions */
/* below other functions */
}
TL;DR
https://googledocs-clone-sbayrak.netlify.app/ is not an origin. Drop that trailing slash.
More details about the problem
No trailing slash allowed in the value of the Origin header
According to the CORS protocol (specified in the Fetch standard), browsers never set the Origin request header to a value with a trailing slash. Therefore, if a page at https://googledocs-clone-sbayrak.netlify.app/whatever issues a cross-origin request, that request's Origin header will contain
https://googledocs-clone-sbayrak.netlify.app
without any trailing slash.
Byte-by-byte comparison on the server side
You're using Socket.IO, which relies on the Node.js cors package. That package won't set any Access-Control-Allow-Origin in the response if the request's origin doesn't exactly match your CORS configuration's origin value (https://googledocs-clone-sbayrak.netlify.app/).
Putting it all together
Obviously,
'https://googledocs-clone-sbayrak.netlify.app' ===
'https://googledocs-clone-sbayrak.netlify.app/'
evaluates to false, which causes the cors package not to set any Access-Control-Allow-Origin header in the response, which causes the CORS check to fail in your browser, hence the CORS error you observed.
Example from the Fetch Standard
Section 3.2.5 of the Fetch Standard even provides an enlightening example of this mistake,
Access-Control-Allow-Origin: https://rabbit.invalid/
and explains why it causes the CORS check to fail:
A serialized origin has no trailing slash.
Looks like you haven't imported the cors package. Is it imported anywhere else?
var cors = require('cors') // is missing

CORS not working on specific domains in firebase cloud functions

const whitelist = ["http://mywebsite.com", "http://localhost:5000"]
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
const cors = require('cors')(corsOptions);
exports.getUserProfile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
try {
//... here we fetch the user profile and send it in response
} catch (err) {
res.json({
status: "error",
message: err
})
}
});
});
I'm using CORS in this way so I could only let my own website and localhost:5000 to make requests to the cloud functions that I'm using. localhost:5000 is for me so I could test stuff locally before I deploy the files.
Making a request from reqbin.com to my cloud function is working, why? Shouldn't it NOT work, since I've specified the domain to be either from mywebsite.com or localhost:5000?
Is there any other way to test stuff locally without having to include localhost:5000 in the whitelist? I'm using firebase serve to test, but I'm assuming this won't work since the requests won't be treated from mywebsite.com
This will help you by using origin from cors library directly.
const cors = require('cors')
const corsHandler = cors({
origin:["http://mywebsite.com", "http://localhost:5000"]
});
exports.getUserProfile = functions.https.onRequest((req, res) => {
corsHandler(req, res, async () => {
try {
//... here we fetch the user profile and send it in response
} catch (err) {
res.json({
status: "error",
message: err
})
}
});
});

Check url-redirection using axios in nodejs

I'm using axios for API calling in nodejs.
I want to check if "https://origin.com/?url=https://destination.com" this url is redirecting to "destination.com".
So which status/headers I can check in axios response.
What will be the condition to check if it goes to "destination.com"
var express = require('express');
const axios = require('axios');
var app = express();
app.get('/test', function (req, res) {
// Check if it redirects
axios.get('https://origin.com/?url=https://destination.com')
.then(response => {
console.log(response);
// Here I want to check if it redirects successfully to "destination.com"
if (condition) {
// Successfully redirected to "destination.com"
}
})
.catch(error => {
console.log(error);
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
I think you should try to use https for api, then in the response header you can find that it direct to the "destination.com".
Https is much simple and easy to use.
const https = require('https');
https.get('https://encrypted.google.com/', (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
}).on('error', (e) => {
console.error(e);
});
You can find the redirect url under responseURL key,
get fetchedUrl by
const fetchedUrl = response.request.res.responseURL;
see this
Environment node: v8.9.4 axios: ^0.18.0

How does node-http-proxy parse the target url?

I ran into a problem and I feel that node-http-proxy is changing my target links. I got a few examples below.
I am using express as my server and using Metaweather API .
The problem is that I was able to get data from the endpoints below
https://www.metaweather.com/api/location/2487956/
https://www.metaweather.com/api/location/2487956/2013/4/30/
But when I try to call the API from https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02
It fails with status code 500 which I lead me thinking that node-http-proxy added some values after 122.02 as it was not closed with /
server.js
const express = require("express");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const httpProxy = require("http-proxy");
const proxyOptions = {
changeOrigin: true
};
const apiProxy = httpProxy.createProxyServer(proxyOptions);
const apiUrl =
"https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02";
/*
https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02 - failed with 500
https://www.metaweather.com/api/location/2487956/ - passed
https://www.metaweather.com/api/location/2487956/2013/4/30/ - passed
*/
app
.prepare()
.then(() => {
const server = express();
server.use("/api", (req, res) => {
console.log("Going to call this API " + apiUrl);
apiProxy.web(req, res, { target: apiUrl });
});
server.get("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, err => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
Thanks for looking into this question.
I have reproduced where this is happening in node-http-proxy.
In common.js there is a function called urlJoin which is appending the req.url to the end of the target url.
I'm not exactly sure what the intent is, but it's a start.
Here's my test:
const urlJoin = function() {
//
// We do not want to mess with the query string. All we want to touch is the path.
//
var args = Array.prototype.slice.call(arguments),
lastIndex = args.length - 1,
last = args[lastIndex],
lastSegs = last.split('?'),
retSegs;
args[lastIndex] = lastSegs.shift();
//
// Join all strings, but remove empty strings so we don't get extra slashes from
// joining e.g. ['', 'am']
//
retSegs = [
args.filter(Boolean).join('/')
.replace(/\/+/g, '/')
.replace('http:/', 'http://')
.replace('https:/', 'https://')
];
// Only join the query string if it exists so we don't have trailing a '?'
// on every request
// Handle case where there could be multiple ? in the URL.
retSegs.push.apply(retSegs, lastSegs);
return retSegs.join('?')
};
let path = urlJoin('/api/location/search/?lattlong=36.96,-122.02', '/');
console.log(path);
// /api/location/search/?lattlong=36.96,-122.02/

Categories