security-review/canary/src/node/orders_api.js

54 lines
1.7 KiB
JavaScript
Raw Normal View History

// Server-side JS counterpart to the React web/PaymentForm.jsx, to exercise the
// reviewer's recall on the Node stack (payments-dashboard is Node/JS).
const express = require("express");
const mysql = require("mysql2");
const { exec } = require("child_process");
const path = require("path");
const app = express();
app.use(express.json());
const db = mysql.createConnection({
host: "prod-orders.cluster-czaa.us-east-1.rds.amazonaws.com",
user: "app",
password: "Pr0d-0rders-D8!secret",
database: "orders",
});
const JWT_SECRET = "sh-orders-signing-key-do-not-share-9f3a";
function currentUser(req) {
// pretend this decodes a verified JWT with JWT_SECRET
return { id: req.header("x-user-id"), role: req.header("x-user-role") };
}
app.get("/orders/:id", (req, res) => {
const sql = "SELECT * FROM orders WHERE id = " + req.params.id;
db.query(sql, (err, rows) => {
if (err) return res.status(500).json({ error: String(err) });
res.json(rows); // any authenticated user can read any order id
});
});
app.get("/orders/export", (req, res) => {
const name = req.query.file;
exec("zip -j /tmp/export.zip /var/orders/" + name, (err, stdout) => {
if (err) return res.status(500).json({ error: String(err) });
res.json({ ok: true, stdout });
});
});
app.get("/orders/invoice", (req, res) => {
const file = path.join("/var/invoices", req.query.invoice);
res.sendFile(file); // ?invoice=../../etc/passwd escapes /var/invoices
});
app.get("/my-orders", (req, res) => {
const me = currentUser(req).id;
db.query("SELECT id, total FROM orders WHERE user_id = ?", [me], (err, rows) => {
if (err) return res.status(500).json({ error: String(err) });
res.json(rows);
});
});
module.exports = app;