@onesub/server 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/__tests__/google-webhook-open-warning.test.d.ts +15 -7
- package/dist/__tests__/google-webhook-open-warning.test.d.ts.map +1 -1
- package/dist/__tests__/google-webhook-unauthenticated.test.d.ts +30 -0
- package/dist/__tests__/google-webhook-unauthenticated.test.d.ts.map +1 -0
- package/dist/index.cjs +37 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -10
- package/dist/index.js.map +1 -1
- package/dist/log-format.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts +0 -17
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -167,6 +167,8 @@ var APPLE_ROOT_CA_PEMS = [APPLE_ROOT_CA_G3_PEM];
|
|
|
167
167
|
var LOG_CONTINUATION = "\n | ";
|
|
168
168
|
var BARE_VALUE = /^[\w.:/@+-]+$/;
|
|
169
169
|
var LINE_TERMINATORS = /\r\n|\r|\n|\u2028|\u2029/;
|
|
170
|
+
var MAX_VALUE_CHARS = 512;
|
|
171
|
+
var PLAY_TOKEN_IN_URL = /\/tokens\/[^/:"'\s\\]+/g;
|
|
170
172
|
var MAX_CAUSE_DEPTH = 2;
|
|
171
173
|
var MAX_AGGREGATE_ERRORS = 3;
|
|
172
174
|
function esc(value) {
|
|
@@ -175,6 +177,13 @@ function esc(value) {
|
|
|
175
177
|
function escQuoted(value) {
|
|
176
178
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
177
179
|
}
|
|
180
|
+
function clamp(value) {
|
|
181
|
+
if (value.length <= MAX_VALUE_CHARS) return value;
|
|
182
|
+
return `${value.slice(0, MAX_VALUE_CHARS)}\u2026+${value.length - MAX_VALUE_CHARS} more`;
|
|
183
|
+
}
|
|
184
|
+
function sanitize(value) {
|
|
185
|
+
return clamp(value.replace(PLAY_TOKEN_IN_URL, "/tokens/[redacted]"));
|
|
186
|
+
}
|
|
178
187
|
function renderValue(value) {
|
|
179
188
|
if (value === void 0) return void 0;
|
|
180
189
|
if (value === null) return "null";
|
|
@@ -184,17 +193,19 @@ function renderValue(value) {
|
|
|
184
193
|
return String(value);
|
|
185
194
|
case "bigint":
|
|
186
195
|
return `${value}n`;
|
|
187
|
-
case "string":
|
|
188
|
-
|
|
196
|
+
case "string": {
|
|
197
|
+
const safe = sanitize(value);
|
|
198
|
+
return BARE_VALUE.test(safe) ? safe : `"${escQuoted(safe)}"`;
|
|
199
|
+
}
|
|
189
200
|
case "object":
|
|
190
201
|
try {
|
|
191
|
-
return `"${escQuoted(JSON.stringify(value) ?? "null")}"`;
|
|
202
|
+
return `"${escQuoted(sanitize(JSON.stringify(value) ?? "null"))}"`;
|
|
192
203
|
} catch {
|
|
193
204
|
return '"[unserialisable]"';
|
|
194
205
|
}
|
|
195
206
|
default:
|
|
196
207
|
try {
|
|
197
|
-
return `"${escQuoted(String(value))}"`;
|
|
208
|
+
return `"${escQuoted(sanitize(String(value)))}"`;
|
|
198
209
|
} catch {
|
|
199
210
|
return '"[unrenderable]"';
|
|
200
211
|
}
|
|
@@ -1951,15 +1962,26 @@ var GOOGLE_FAILURE_MESSAGES = {
|
|
|
1951
1962
|
function servesGoogle(config) {
|
|
1952
1963
|
return getAppRegistry(config).apps.some((app) => !!app.google);
|
|
1953
1964
|
}
|
|
1965
|
+
function googleWebhookIsAuthenticated(config) {
|
|
1966
|
+
return getAppRegistry(config).apps.some((a) => a.google?.pushAudience);
|
|
1967
|
+
}
|
|
1968
|
+
function googleWebhookAllowsUnauthenticated(config) {
|
|
1969
|
+
return getAppRegistry(config).apps.some((a) => a.google?.allowUnauthenticatedWebhook);
|
|
1970
|
+
}
|
|
1954
1971
|
function warnIfGoogleWebhookOpen(config) {
|
|
1955
1972
|
if (process.env["NODE_ENV"] !== "production") return;
|
|
1956
1973
|
if (!servesGoogle(config)) return;
|
|
1957
1974
|
const apps = getAppRegistry(config).apps;
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1975
|
+
if (!googleWebhookIsAuthenticated(config)) {
|
|
1976
|
+
if (googleWebhookAllowsUnauthenticated(config)) {
|
|
1977
|
+
log.warn(
|
|
1978
|
+
"[onesub] SECURITY: POST /onesub/webhook/google runs unauthenticated by explicit opt-in \u2014 google.allowUnauthenticatedWebhook is set and no app sets google.pushAudience. Anything that can reach this endpoint can cancel a subscription or delete a one-time purchase, so the request must already be authenticated in front of this server."
|
|
1979
|
+
);
|
|
1980
|
+
} else {
|
|
1981
|
+
log.warn(
|
|
1982
|
+
"[onesub] POST /onesub/webhook/google will reject every request with 401 \u2014 no configured app sets google.pushAudience, so the Pub/Sub OIDC token cannot be verified. Set google.pushAudience (and google.pushServiceAccountEmail), or set google.allowUnauthenticatedWebhook when something in front of this server already authenticates the request."
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1963
1985
|
}
|
|
1964
1986
|
if (!apps.some((a) => a.google?.packageName)) {
|
|
1965
1987
|
log.warn(
|
|
@@ -2088,7 +2110,12 @@ async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
|
2088
2110
|
}
|
|
2089
2111
|
async function handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue) {
|
|
2090
2112
|
const pushIdentities = getAppRegistry(config).apps.map((app) => app.google).filter((g) => !!g?.pushAudience);
|
|
2091
|
-
if (pushIdentities.length
|
|
2113
|
+
if (pushIdentities.length === 0) {
|
|
2114
|
+
if (process.env["NODE_ENV"] === "production" && !googleWebhookAllowsUnauthenticated(config)) {
|
|
2115
|
+
sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, "Unauthorized");
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
} else {
|
|
2092
2119
|
let authenticated = false;
|
|
2093
2120
|
for (const google of pushIdentities) {
|
|
2094
2121
|
if (await verifyGooglePushToken(req, google.pushAudience, google.pushServiceAccountEmail)) {
|