diff --git a/.snyk b/.snyk new file mode 100644 index 00000000000..3660ac989c6 --- /dev/null +++ b/.snyk @@ -0,0 +1,10 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.25.0 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-UNDERSCORE-1080984: + - '*': + reason: test12 + expires: 2024-10-19T19:37:24.536Z + created: 2024-09-19T19:37:24.537Z +patch: {} diff --git a/catalog-info.yaml b/catalog-info.yaml new file mode 100644 index 00000000000..c86c90fee45 --- /dev/null +++ b/catalog-info.yaml @@ -0,0 +1,126 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: nodejs-goof + title: NodeJS Goof Service + description: | + NodeJS service + labels: + tier: "3" + links: + - url: https://www.notion.so/roadie/nodejs-goof-e067465e56a14ae9b0507f8559f6aa26 + title: Datadog + icon: dashboard + tags: + - test + - js + - demo + annotations: + github.com/project-slug: snyk-cs-goof-org/nodejs-goof + # The Sentry organization is stored in the app-config.yaml of the Backstage instance. + sentry.io/project-slug: nodejs-goof + cloud.google.com/function-ids: projects/roadie-dev-283705/locations/europe-west1/functions/test-fn-1 + backstage.io/techdocs-ref: dir:. + pagerduty.com/service-id: PZDAG59 + snyk.io/org-id: a0e0775e-2b17-4e57-8d85-6c8fbe5bc9a0 + backstage.io/kubernetes-id: nodejs-goof + travis-ci.com/repo-slug: snyk-cs-goof-org/nodejs-goof + backstage.io/kubernetes-label-selector: 'app.kubernetes.io/name=nodejs-goof' + opsgenie.com/component-selector: nodejs-goof-1 + qameta.io/allure-project: value + newrelic.com/dashboard-guid: 'MzcwMjEwN3xWSVp8REFTSEJPQVJEfGRhOjkwMDk4' + sonarqube.org/project-key: snyk-cs-goof-org_nodejs-goof + datadoghq.com/tag-name: nodejs-goof + datadoghq.com/slo_tag: service:nodejs-goof + datadoghq.com/monitor_tags: nodejs-goof + backstage.io/code-coverage: enabled + endoflife.date/products: nodejs +spec: + type: service + owner: group:snyk-cs-goof-org/cs-goof-back-end + lifecycle: experimental + system: nodeJSGoof + providesApis: + - python-goof + dependsOn: + - Component:python-goof-2 + - Resource:sample-s3-bucket + +--- +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + title: Sentry JS + name: sentry-js + description: A JS client which communicates with the Sentry HTTP API. +spec: + type: library + lifecycle: production + owner: group:roadiehq/marketing + system: nodeJSGoof + subcomponentOf: python-goof + +--- +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + title: Sample Service API + name: python-goof + description: The Sample Service API allows you to send errors to Rollbar or Sentry. + tags: + - unstable +spec: + type: openapi + lifecycle: production + owner: group:snyk-cs-goof-org/cs-goof-back-end + system: nodeJSGoof + definition: + $text: https://github.com/snyk-cs-goof/nodejs-goof/blob/main/api-spec.yaml + +--- +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + title: Sample OpenApi Ref API + name: open-api-ref-sample + description: Sample catalog file referencing Petstore openapi spec + tags: + - unstable +spec: + type: openapi + lifecycle: production + owner: group:snyk-cs-goof-org/cs-goof-back-end + system: nodeJSGoof + definition: + $text: https://petstore.swagger.io/v2/swagger.json + +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: sample-s3-bucket + description: The s3 bucket with the sample data in it. Doesn't really exist. +spec: + type: s3-bucket + owner: team-alpha + system: nodeJSGoof + +--- +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: sample-domain + description: Sample domain which has stuff for demoing. +spec: + owner: group:snyk-cs-goof-org/cs-goof-back-end + +--- +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + title: NodeJS Goof + name: nodeJSGoof + description: NodeJS Goof Service +spec: + owner: group:snyk-cs-goof-org/cs-goof-back-end + domain: sample-domain diff --git a/package-lock.json b/package-lock.json index fef2be20353..b05363bc157 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.1", "license": "Apache-2.0", "dependencies": { - "adm-zip": "0.4.7", + "adm-zip": "^0.4.7", "body-parser": "1.9.0", "cfenv": "^1.0.4", "consolidate": "0.14.5", @@ -332,9 +332,10 @@ } }, "node_modules/adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", + "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==", + "license": "MIT", "engines": { "node": ">=0.3.0" } @@ -12832,9 +12833,9 @@ "dev": true }, "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=" + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", + "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==" }, "agent-base": { "version": "4.3.0", diff --git a/package.json b/package.json index d5f9362a36d..755181715a3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "test": "snyk test" }, "dependencies": { - "adm-zip": "0.4.7", + "adm-zip": "0.4.11", "body-parser": "1.9.0", "cfenv": "^1.0.4", "consolidate": "0.14.5", diff --git a/routes/index.js b/routes/index.js deleted file mode 100644 index 6b5455f03e4..00000000000 --- a/routes/index.js +++ /dev/null @@ -1,367 +0,0 @@ -var utils = require('../utils'); -var mongoose = require('mongoose'); -var Todo = mongoose.model('Todo'); -var User = mongoose.model('User'); -// TODO: -var hms = require('humanize-ms'); -var ms = require('ms'); -var streamBuffers = require('stream-buffers'); -var readline = require('readline'); -var moment = require('moment'); -var exec = require('child_process').exec; -var validator = require('validator'); - -// zip-slip -var fileType = require('file-type'); -var AdmZip = require('adm-zip'); -var fs = require('fs'); - -// prototype-pollution -var _ = require('lodash'); - -exports.index = function (req, res, next) { - Todo. - find({}). - sort('-updated_at'). - exec(function (err, todos) { - if (err) return next(err); - - res.render('index', { - title: 'Patch TODO List', - subhead: 'Vulnerabilities at their best', - todos: todos, - }); - }); -}; - -exports.loginHandler = function (req, res, next) { - if (validator.isEmail(req.body.username)) { - User.find({ username: req.body.username, password: req.body.password }, function (err, users) { - if (users.length > 0) { - const redirectPage = req.body.redirectPage - const session = req.session - const username = req.body.username - return adminLoginSuccess(redirectPage, session, username, res) - } else { - return res.status(401).send() - } - }); - } else { - return res.status(401).send() - } -}; - -function adminLoginSuccess(redirectPage, session, username, res) { - session.loggedIn = 1 - - // Log the login action for audit - console.log(`User logged in: ${username}`) - - if (redirectPage) { - return res.redirect(redirectPage) - } else { - return res.redirect('/admin') - } -} - -exports.login = function (req, res, next) { - return res.render('admin', { - title: 'Admin Access', - granted: false, - redirectPage: req.query.redirectPage - }); -}; - -exports.admin = function (req, res, next) { - return res.render('admin', { - title: 'Admin Access Granted', - granted: true, - }); -}; - -exports.get_account_details = function(req, res, next) { - // @TODO need to add a database call to get the profile from the database - // and provide it to the view to display - const profile = {} - return res.render('account.hbs', profile) -} - -exports.save_account_details = function(req, res, next) { - // get the profile details from the JSON - const profile = req.body - // validate the input - if (validator.isEmail(profile.email, { allow_display_name: true }) - // allow_display_name allows us to receive input as: - // Display Name - // which we consider valid too - && validator.isMobilePhone(profile.phone, 'he-IL') - && validator.isAscii(profile.firstname) - && validator.isAscii(profile.lastname) - && validator.isAscii(profile.country) - ) { - // trim any extra spaces on the right of the name - profile.firstname = validator.rtrim(profile.firstname) - profile.lastname = validator.rtrim(profile.lastname) - - // render the view - return res.render('account.hbs', profile) - } else { - // if input validation fails, we just render the view as is - console.log('error in form details') - return res.render('account.hbs') - } -} - -exports.isLoggedIn = function (req, res, next) { - if (req.session.loggedIn === 1) { - return next() - } else { - return res.redirect('/') - } -} - -exports.logout = function (req, res, next) { - req.session.loggedIn = 0 - req.session.destroy(function() { - return res.redirect('/') - }) -} - -function parse(todo) { - var t = todo; - - var remindToken = ' in '; - var reminder = t.toString().indexOf(remindToken); - if (reminder > 0) { - var time = t.slice(reminder + remindToken.length); - time = time.replace(/\n$/, ''); - - var period = hms(time); - - console.log('period: ' + period); - - // remove it - t = t.slice(0, reminder); - if (typeof period != 'undefined') { - t += ' [' + ms(period) + ']'; - } - } - return t; -} - -exports.create = function (req, res, next) { - // console.log('req.body: ' + JSON.stringify(req.body)); - - var item = req.body.content; - var imgRegex = /\!\[alt text\]\((http.*)\s\".*/; - if (typeof (item) == 'string' && item.match(imgRegex)) { - var url = item.match(imgRegex)[1]; - console.log('found img: ' + url); - - exec('identify ' + url, function (err, stdout, stderr) { - console.log(err); - if (err !== null) { - console.log('Error (' + err + '):' + stderr); - } - }); - - } else { - item = parse(item); - } - - new Todo({ - content: item, - updated_at: Date.now(), - }).save(function (err, todo, count) { - if (err) return next(err); - - /* - res.setHeader('Data', todo.content.toString('base64')); - res.redirect('/'); - */ - - res.setHeader('Location', '/'); - res.status(302).send(todo.content.toString('base64')); - - // res.redirect('/#' + todo.content.toString('base64')); - }); -}; - -exports.destroy = function (req, res, next) { - Todo.findById(req.params.id, function (err, todo) { - - try { - todo.remove(function (err, todo) { - if (err) return next(err); - res.redirect('/'); - }); - } catch (e) { - } - }); -}; - -exports.edit = function (req, res, next) { - Todo. - find({}). - sort('-updated_at'). - exec(function (err, todos) { - if (err) return next(err); - - res.render('edit', { - title: 'TODO', - todos: todos, - current: req.params.id - }); - }); -}; - -exports.update = function (req, res, next) { - Todo.findById(req.params.id, function (err, todo) { - - todo.content = req.body.content; - todo.updated_at = Date.now(); - todo.save(function (err, todo, count) { - if (err) return next(err); - - res.redirect('/'); - }); - }); -}; - -// ** express turns the cookie key to lowercase ** -exports.current_user = function (req, res, next) { - - next(); -}; - -function isBlank(str) { - return (!str || /^\s*$/.test(str)); -} - -exports.import = function (req, res, next) { - if (!req.files) { - res.send('No files were uploaded.'); - return; - } - - var importFile = req.files.importFile; - var data; - var importedFileType = fileType(importFile.data); - var zipFileExt = { ext: "zip", mime: "application/zip" }; - if (importedFileType === null) { - importedFileType = { ext: "txt", mime: "text/plain" }; - } - if (importedFileType["mime"] === zipFileExt["mime"]) { - var zip = AdmZip(importFile.data); - var extracted_path = "/tmp/extracted_files"; - zip.extractAllTo(extracted_path, true); - data = "No backup.txt file found"; - fs.readFile('backup.txt', 'ascii', function (err, data) { - if (!err) { - data = data; - } - }); - } else { - data = importFile.data.toString('ascii'); - } - var lines = data.split('\n'); - lines.forEach(function (line) { - var parts = line.split(','); - var what = parts[0]; - console.log('importing ' + what); - var when = parts[1]; - var locale = parts[2]; - var format = parts[3]; - var item = what; - if (!isBlank(what)) { - if (!isBlank(when) && !isBlank(locale) && !isBlank(format)) { - console.log('setting locale ' + parts[1]); - moment.locale(locale); - var d = moment(when); - console.log('formatting ' + d); - item += ' [' + d.format(format) + ']'; - } - - new Todo({ - content: item, - updated_at: Date.now(), - }).save(function (err, todo, count) { - if (err) return next(err); - console.log('added ' + todo); - }); - } - }); - - res.redirect('/'); -}; - -exports.about_new = function (req, res, next) { - console.log(JSON.stringify(req.query)); - return res.render("about_new.dust", - { - title: 'Patch TODO List', - subhead: 'Vulnerabilities at their best', - device: req.query.device - }); -}; - -// Prototype Pollution - -/////////////////////////////////////////////////////////////////////////////// -// In order of simplicity we are not using any database. But you can write the -// same logic using MongoDB. -const users = [ - // You know password for the user. - { name: 'user', password: 'pwd' }, - // You don't know password for the admin. - { name: 'admin', password: Math.random().toString(32), canDelete: true }, -]; - -let messages = []; -let lastId = 1; - -function findUser(auth) { - return users.find((u) => - u.name === auth.name && - u.password === auth.password); -} -/////////////////////////////////////////////////////////////////////////////// - -exports.chat = { - get(req, res) { - res.send(messages); - }, - add(req, res) { - const user = findUser(req.body.auth || {}); - - if (!user) { - res.status(403).send({ ok: false, error: 'Access denied' }); - return; - } - - const message = { - // Default message icon. Cen be overwritten by user. - icon: '👋', - }; - - _.merge(message, req.body.message, { - id: lastId++, - timestamp: Date.now(), - userName: user.name, - }); - - messages.push(message); - res.send({ ok: true }); - }, - delete(req, res) { - const user = findUser(req.body.auth || {}); - - if (!user || !user.canDelete) { - res.status(403).send({ ok: false, error: 'Access denied' }); - return; - } - - messages = messages.filter((m) => m.id !== req.body.messageId); - res.send({ ok: true }); - } -};