index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. const crypto = require('crypto');
  2. const bodyParser = require('body-parser');
  3. const helmet = require('helmet');
  4. const uaparser = require('ua-parser-js');
  5. const storage = require('../storage');
  6. const config = require('../config');
  7. const auth = require('../middleware/auth');
  8. const language = require('../middleware/language');
  9. const pages = require('./pages');
  10. const filelist = require('./filelist');
  11. const clientConstants = require('../clientConstants');
  12. const IS_DEV = config.env === 'development';
  13. const ID_REGEX = '([0-9a-fA-F]{10,16})';
  14. module.exports = function(app) {
  15. app.set('trust proxy', true);
  16. app.use(helmet());
  17. app.use(
  18. helmet.hsts({
  19. maxAge: 31536000,
  20. force: !IS_DEV
  21. })
  22. );
  23. app.use(function(req, res, next) {
  24. req.ua = uaparser(req.header('user-agent'));
  25. next();
  26. });
  27. app.use(function(req, res, next) {
  28. req.cspNonce = crypto.randomBytes(16).toString('hex');
  29. next();
  30. });
  31. if (!IS_DEV) {
  32. let csp = {
  33. directives: {
  34. defaultSrc: ["'self'"],
  35. connectSrc: [
  36. "'self'",
  37. 'wss://*.dev.lcip.org',
  38. 'wss://*.send.nonprod.cloudops.mozgcp.net',
  39. config.base_url.replace(/^https:\/\//, 'wss://'),
  40. 'https://*.dev.lcip.org',
  41. 'https://accounts.firefox.com',
  42. 'https://*.accounts.firefox.com',
  43. 'https://sentry.prod.mozaws.net'
  44. ],
  45. imgSrc: [
  46. "'self'",
  47. 'https://*.dev.lcip.org',
  48. 'https://firefoxusercontent.com',
  49. 'https://secure.gravatar.com'
  50. ],
  51. scriptSrc: [
  52. "'self'",
  53. function(req) {
  54. return `'nonce-${req.cspNonce}'`;
  55. }
  56. ],
  57. formAction: ["'none'"],
  58. frameAncestors: ["'none'"],
  59. objectSrc: ["'none'"],
  60. reportUri: '/__cspreport__'
  61. }
  62. }
  63. csp.directives.connectSrc.push(config.base_url.replace(/^https:\/\//,'wss://'))
  64. if(config.fxa_csp_oauth_url != ""){
  65. csp.directives.connectSrc.push(config.fxa_csp_oauth_url)
  66. }
  67. if(config.fxa_csp_content_url != "" ){
  68. csp.directives.connectSrc.push(config.fxa_csp_content_url)
  69. }
  70. if(config.fxa_csp_profile_url != "" ){
  71. csp.directives.connectSrc.push(config.fxa_csp_profile_url)
  72. }
  73. if(config.fxa_csp_profileimage_url != ""){
  74. csp.directives.imgSrc.push(config.fxa_csp_profileimage_url)
  75. }
  76. app.use(
  77. helmet.contentSecurityPolicy(csp)
  78. );
  79. }
  80. app.use(function(req, res, next) {
  81. res.set('Pragma', 'no-cache');
  82. res.set(
  83. 'Cache-Control',
  84. 'private, no-cache, no-store, must-revalidate, max-age=0'
  85. );
  86. next();
  87. });
  88. app.use(bodyParser.json());
  89. app.use(bodyParser.text());
  90. app.get('/', language, pages.index);
  91. app.get('/config', function(req, res) {
  92. res.json(clientConstants);
  93. });
  94. app.get('/error', language, pages.blank);
  95. app.get('/oauth', language, pages.blank);
  96. app.get('/legal', language, pages.legal);
  97. app.get('/login', language, pages.index);
  98. app.get('/app.webmanifest', language, require('./webmanifest'));
  99. app.get(`/download/:id${ID_REGEX}`, language, pages.download);
  100. app.get('/unsupported/:reason', language, pages.unsupported);
  101. app.get(`/api/download/:id${ID_REGEX}`, auth.hmac, require('./download'));
  102. app.get(
  103. `/api/download/blob/:id${ID_REGEX}`,
  104. auth.hmac,
  105. require('./download')
  106. );
  107. app.get(`/api/exists/:id${ID_REGEX}`, require('./exists'));
  108. app.get(`/api/metadata/:id${ID_REGEX}`, auth.hmac, require('./metadata'));
  109. app.get('/api/filelist/:id([\\w-]{16})', auth.fxa, filelist.get);
  110. app.post('/api/filelist/:id([\\w-]{16})', auth.fxa, filelist.post);
  111. app.post('/api/upload', auth.fxa, require('./upload'));
  112. app.post(`/api/delete/:id${ID_REGEX}`, auth.owner, require('./delete'));
  113. app.post(`/api/password/:id${ID_REGEX}`, auth.owner, require('./password'));
  114. app.post(
  115. `/api/params/:id${ID_REGEX}`,
  116. auth.owner,
  117. auth.fxa,
  118. require('./params')
  119. );
  120. app.post(`/api/info/:id${ID_REGEX}`, auth.owner, require('./info'));
  121. app.post('/api/metrics', require('./metrics'));
  122. app.get('/__version__', function(req, res) {
  123. // eslint-disable-next-line node/no-missing-require
  124. res.sendFile(require.resolve('../../dist/version.json'));
  125. });
  126. app.get('/__lbheartbeat__', function(req, res) {
  127. res.sendStatus(200);
  128. });
  129. app.get('/__heartbeat__', async (req, res) => {
  130. try {
  131. await storage.ping();
  132. res.sendStatus(200);
  133. } catch (e) {
  134. res.sendStatus(500);
  135. }
  136. });
  137. };