63 lines
2.7 KiB
JavaScript
63 lines
2.7 KiB
JavaScript
// 回归测试 #3:订单查询 IDOR。GET /store/order/:id 只有携带正确的一次性 pollSecret
|
|
// 才下发 redeemToken —— 枚举订单 id 偷不到他人登录令牌。
|
|
// 需要本地 MongoDB;连不上则整组 skip(不阻塞无 DB 的 CI)。
|
|
process.env.NODE_ENV = 'test'; // 非生产 → stub checkout 会立即开通,便于构造已支付订单
|
|
process.env.MONGO_DB = process.env.MONGO_DB_TEST || 'digital_me_test';
|
|
|
|
import { test, before, after } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import express from 'express';
|
|
import http from 'node:http';
|
|
|
|
let server, base, db, closeDb, mongoUp = true;
|
|
|
|
before(async () => {
|
|
const dbm = await import('../src/db.js');
|
|
closeDb = dbm.closeDb;
|
|
try { db = await dbm.connectDb(); }
|
|
catch { mongoUp = false; return; }
|
|
const { storeRouter } = await import('../src/routes/store.js');
|
|
const app = express();
|
|
app.use(express.json());
|
|
app.use('/api/store', storeRouter);
|
|
server = http.createServer(app).listen(0);
|
|
await new Promise((ok) => server.once('listening', ok));
|
|
base = `http://127.0.0.1:${server.address().port}`;
|
|
});
|
|
|
|
after(async () => {
|
|
if (server) server.close();
|
|
if (mongoUp && closeDb) await closeDb();
|
|
});
|
|
|
|
const j = async (p, opts) => (await fetch(base + p, opts)).json();
|
|
|
|
test('order IDOR:无/错 secret 不下发 redeemToken,正确 secret 才下发', async (t) => {
|
|
if (!mongoUp) return t.skip('无本地 MongoDB,跳过 DB 集成测试');
|
|
const prods = await j('/api/store/products');
|
|
const sku = (prods.products.find((p) => p.available) || {}).id;
|
|
if (!sku) return t.skip('无可售商品');
|
|
|
|
const co = await j('/api/store/checkout', {
|
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ sku, email: `buyer_${Date.now()}@example.com` }),
|
|
});
|
|
assert.ok(co.orderId, 'checkout 应返回 orderId');
|
|
|
|
// 攻击者视角:只有订单 id
|
|
const noSecret = await j(`/api/store/order/${co.orderId}`);
|
|
assert.equal(noSecret.status, 'paid');
|
|
assert.equal(noSecret.redeemToken, '', 'IDOR:无 secret 不应下发 redeemToken');
|
|
|
|
const wrong = await j(`/api/store/order/${co.orderId}?secret=not-the-secret`);
|
|
assert.equal(wrong.redeemToken, '', '错误 secret 也不应下发');
|
|
|
|
// 本人视角:拿到正确 pollSecret(stub 响应里不含,从 DB 取真实值)
|
|
const { ObjectId } = await import('mongodb');
|
|
const order = await db.collection('store_orders').findOne({ _id: new ObjectId(co.orderId) });
|
|
const ok = await j(`/api/store/order/${co.orderId}?secret=${order.pollSecret}`);
|
|
assert.ok(ok.redeemToken && ok.redeemToken.length > 0, '正确 secret 应下发 redeemToken');
|
|
|
|
await db.collection('store_orders').deleteOne({ _id: new ObjectId(co.orderId) });
|
|
});
|