openvibers/openvibe.coupons Commits

Coupons lookups that differ for a browser extension are never kept by the CDN, so an extension can never be handed a cached page-caller answer with DealNews codes in it (#80)

OP OpenVibers committed 22 days ago · 6decea15a3 · 2 files, +15 −3

Changes

ddiff --git a/server/http/api.js b/server/http/api.js
iindex 47900d5..1f756ad 100644
---- a/server/http/api.js
++++ b/server/http/api.js
@@@ -55,9 +55,20 @@ function createApi(ctx) {
return config.extensionOrigins.includes('moz-extension://*') && MOZ_EXTENSION_RE.test(origin);
}
++ /**
++ * These answers differ for a browser extension (source-policy.js), and Cloudflare keys its cache on the URL alone
++ * (it ignores Vary): the edge must never keep one, or an extension could be handed the page-caller's answer.
++ * Browsers still cache them (Cache-Control, Vary: Origin).
++ */
++ function byOrigin(res) {
++ res.vary('Origin');
++ res.set('CDN-Cache-Control', 'no-store');
++ res.set('Cloudflare-CDN-Cache-Control', 'no-store');
++ }
++
/** CORS for the lookup routes only: exact configured extension origins, never credentials. */
function lookupCors(req, res, next) {
-- res.vary('Origin');
++ byOrigin(res);
const origin = req.get('origin');
if (extensionOriginAllowed(origin)) {
res.set('Access-Control-Allow-Origin', origin);
@@@ -154,7 +165,7 @@ function createApi(ctx) {
router.get('/merchants/:id', lookupLimiter, guard(CAPABILITIES.COUPON_LOOKUP, { scope: SCOPES.LOOKUP }), reads, run(async (req, res) => {
const m = await activeMerchant(req.params.id);
-- res.vary('Origin');
++ byOrigin(res);
publicCache(res);
return { merchant: await merchantView(m, req) };
}));
@@@ -164,7 +175,7 @@ function createApi(ctx) {
const m = c && await merchants.byId(c.merchant_id);
if (!c || !m || m.status !== 'active' || c.review_state !== 'published' || c.status === 'disabled') throw new ApiError(404, 'coupon.not_found', 'no such code');
const v = await coupons.view(c, { merchant: m });
-- res.vary('Origin');
++ byOrigin(res);
if (v.extension_excluded && fromExtension(req)) throw new ApiError(404, 'coupon.not_found', 'no such code');
publicCache(res);
return { coupon: v, merchant: await merchantView(m, req) };
ddiff --git a/test/sources.test.js b/test/sources.test.js
iindex 39f1bb0..a8f1fb0 100644
---- a/test/sources.test.js
++++ b/test/sources.test.js
@@@ -125,6 +125,7 @@ const { boot, check, done } = require('./helpers/boot');
assert.ok(!ext.json().coupons.some((x) => x.code === 'UXBMXG9V') && ext.json().merchant.active_codes === 0, `an extension never does (${headers.origin})`);
assert.strictEqual((await t.get(`/api/v1/coupons/${c.id}`, { headers })).status, 404);
assert.match(ext.headers.get('vary') || '', /Origin/);
++ assert.strictEqual(ext.headers.get('cloudflare-cdn-cache-control'), 'no-store', 'the edge never keeps an answer that differs by caller');
}
});