@paywalls-net/filter 1.3.8 → 1.3.9
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/package.json +1 -1
- package/src/index.js +57 -27
- package/src/user-agent-classification.js +2 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -77,6 +77,42 @@ function isVAIRequest(request, vaiPath = '/pw') {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Clean 1:1 header forwarding map for operational proxy headers (§7.2).
|
|
82
|
+
* Each entry: { src: string (lowercase incoming), dest: string (outgoing) }
|
|
83
|
+
* Headers with fallback logic or multi-source derivation are handled separately.
|
|
84
|
+
*/
|
|
85
|
+
const PROXY_HEADER_MAP = [
|
|
86
|
+
{ src: 'host', dest: 'X-Original-Host' }, // publisher hostname for domain binding
|
|
87
|
+
{ src: 'origin', dest: 'X-Forwarded-Origin' }, // relay for CORS evaluation (§5, §7.2)
|
|
88
|
+
{ src: 'access-control-request-method', dest: 'Access-Control-Request-Method' }, // preflight (§5.4)
|
|
89
|
+
{ src: 'access-control-request-headers',dest: 'Access-Control-Request-Headers' }, // preflight (§5.4)
|
|
90
|
+
{ src: 'cookie', dest: 'Cookie' }, // session/identity context
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Maps browser signal sources → X-PW-* forwarding headers (§5.2).
|
|
95
|
+
* Each entry: { from: 'headers'|'cf', src: string, dest: string }
|
|
96
|
+
* from:'headers' — read from incoming request headers (lowercase)
|
|
97
|
+
* from:'cf' — read from request.cf property
|
|
98
|
+
*/
|
|
99
|
+
const SIGNAL_HEADER_MAP = [
|
|
100
|
+
// Bundle A: Sec-Fetch (3 pts)
|
|
101
|
+
{ from: 'headers', src: 'sec-fetch-dest', dest: 'X-PW-Sec-Fetch-Dest' },
|
|
102
|
+
{ from: 'headers', src: 'sec-fetch-mode', dest: 'X-PW-Sec-Fetch-Mode' },
|
|
103
|
+
{ from: 'headers', src: 'sec-fetch-site', dest: 'X-PW-Sec-Fetch-Site' },
|
|
104
|
+
// Bundle B: Accept (2 pts)
|
|
105
|
+
{ from: 'headers', src: 'accept', dest: 'X-PW-Accept' },
|
|
106
|
+
{ from: 'headers', src: 'accept-language', dest: 'X-PW-Accept-Language' },
|
|
107
|
+
{ from: 'headers', src: 'accept-encoding', dest: 'X-PW-Accept-Encoding' },
|
|
108
|
+
// Bundle C: Client Hints (2 pts)
|
|
109
|
+
{ from: 'headers', src: 'sec-ch-ua', dest: 'X-PW-Sec-CH-UA' },
|
|
110
|
+
// Bundle D: CF infrastructure (1 pt) — only valid at first-hop CF Worker
|
|
111
|
+
{ from: 'cf', src: 'tlsVersion', dest: 'X-PW-TLS-Version' },
|
|
112
|
+
{ from: 'cf', src: 'httpProtocol', dest: 'X-PW-HTTP-Protocol' },
|
|
113
|
+
{ from: 'cf', src: 'asn', dest: 'X-PW-ASN' },
|
|
114
|
+
];
|
|
115
|
+
|
|
80
116
|
/**
|
|
81
117
|
* Proxy VAI requests to the cloud-api service (Spec §7).
|
|
82
118
|
*
|
|
@@ -92,6 +128,11 @@ function isVAIRequest(request, vaiPath = '/pw') {
|
|
|
92
128
|
* - User-Agent, X-Forwarded-For: standard proxy headers
|
|
93
129
|
* - Authorization: publisher API key (§7.4)
|
|
94
130
|
*
|
|
131
|
+
* Human-confidence signal forwarding (§5.2):
|
|
132
|
+
* Driven by SIGNAL_HEADER_MAP — each entry specifies a source ('headers' or 'cf')
|
|
133
|
+
* and property name to read, and the X-PW-* destination header to write.
|
|
134
|
+
* Simple passthrough: present values forwarded, absent values omitted.
|
|
135
|
+
*
|
|
95
136
|
* Response passthrough (§7.3):
|
|
96
137
|
* All response headers from cloud-api are returned unchanged — including
|
|
97
138
|
* Access-Control-*, Vary, Cache-Control. The proxy never injects or
|
|
@@ -118,40 +159,29 @@ async function proxyVAIRequest(cfg, request) {
|
|
|
118
159
|
'Authorization': `Bearer ${cfg.paywallsAPIKey}`
|
|
119
160
|
};
|
|
120
161
|
|
|
121
|
-
// Client IP forwarding
|
|
162
|
+
// Client IP forwarding — dual-source, so handled explicitly
|
|
122
163
|
if (headers['x-forwarded-for']) {
|
|
123
164
|
forwardHeaders['X-Forwarded-For'] = headers['x-forwarded-for'];
|
|
124
165
|
} else if (headers['cf-connecting-ip']) {
|
|
125
166
|
forwardHeaders['X-Forwarded-For'] = headers['cf-connecting-ip'];
|
|
126
167
|
}
|
|
127
|
-
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
forwardHeaders['X-Original-Host'] = headers['host'];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Forward browser Origin via custom header for CORS evaluation (§5, §7.2).
|
|
135
|
-
// Cloudflare Workers runtime controls the outbound Origin header on fetch(),
|
|
136
|
-
// so we relay the browser's Origin via X-Forwarded-Origin. Cloud-api's
|
|
137
|
-
// evaluateCORS() reads this to make the authoritative CORS decision.
|
|
138
|
-
if (headers['origin']) {
|
|
139
|
-
forwardHeaders['X-Forwarded-Origin'] = headers['origin'];
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Forward preflight headers so cloud-api can evaluate OPTIONS (§5.4, §7.2)
|
|
143
|
-
if (headers['access-control-request-method']) {
|
|
144
|
-
forwardHeaders['Access-Control-Request-Method'] = headers['access-control-request-method'];
|
|
145
|
-
}
|
|
146
|
-
if (headers['access-control-request-headers']) {
|
|
147
|
-
forwardHeaders['Access-Control-Request-Headers'] = headers['access-control-request-headers'];
|
|
168
|
+
|
|
169
|
+
// Clean 1:1 operational header forwarding (§7.2)
|
|
170
|
+
for (const { src, dest } of PROXY_HEADER_MAP) {
|
|
171
|
+
if (headers[src]) forwardHeaders[dest] = headers[src];
|
|
148
172
|
}
|
|
149
|
-
|
|
150
|
-
// Forward
|
|
151
|
-
|
|
152
|
-
|
|
173
|
+
|
|
174
|
+
// Forward browser-provenance signals as X-PW-* headers (§5.2).
|
|
175
|
+
// Simple passthrough: forward whatever is present, no cross-request state.
|
|
176
|
+
const cf = request.cf || {};
|
|
177
|
+
const sources = { headers, cf };
|
|
178
|
+
for (const { from, src, dest } of SIGNAL_HEADER_MAP) {
|
|
179
|
+
const value = sources[from][src];
|
|
180
|
+
if (value != null && value !== '') {
|
|
181
|
+
forwardHeaders[dest] = String(value);
|
|
182
|
+
}
|
|
153
183
|
}
|
|
154
|
-
|
|
184
|
+
|
|
155
185
|
// Forward request to cloud-api
|
|
156
186
|
const response = await fetch(`${cfg.paywallsAPIHost}${cloudApiPath}`, {
|
|
157
187
|
method: request.method || 'GET',
|