const validateNpmPackageName = require("validate-npm-package-name"); const parsePackageURL = require("../utils/parsePackageURL"); const createSearch = require("./utils/createSearch"); const KnownQueryParams = { main: true, meta: true, module: true }; function isKnownQueryParam(param) { return !!KnownQueryParams[param]; } function queryIsKnown(query) { return Object.keys(query).every(isKnownQueryParam); } function sanitizeQuery(query) { const saneQuery = {}; Object.keys(query).forEach(param => { if (isKnownQueryParam(param)) saneQuery[param] = query[param]; }); return saneQuery; } /** * Parse and validate the URL. */ function parseURL(req, res, next) { // Redirect /_meta/path to /path?meta. if (req.path.match(/^\/_meta\//)) { req.query.meta = ""; return res.redirect(302, req.path.substr(6) + createSearch(req.query)); } // Redirect /path?json => /path?meta if (req.query.json != null) { delete req.query.json; req.query.meta = ""; return res.redirect(302, req.path + createSearch(req.query)); } // Redirect requests with unknown query params to their equivalents // with only known params so they can be served from the cache. This // prevents people using random query params designed to bust the cache. if (!queryIsKnown(req.query)) { return res.redirect(302, req.path + createSearch(sanitizeQuery(req.query))); } const url = parsePackageURL(req.url); // Disallow invalid URLs. if (url == null) { return res .status(403) .type("text") .send(`Invalid URL: ${req.url}`); } const nameErrors = validateNpmPackageName(url.packageName).errors; // Disallow invalid package names. if (nameErrors) { const reason = nameErrors.join(", "); return res .status(403) .type("text") .send(`Invalid package name "${url.packageName}" (${reason})`); } req.packageName = url.packageName; req.packageVersion = url.packageVersion; req.packageSpec = `${url.packageName}@${url.packageVersion}`; req.pathname = url.pathname; req.filename = url.filename; req.search = url.search; req.query = url.query; next(); } module.exports = parseURL;