@obfious/js 0.1.7 → 0.1.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/README.md CHANGED
@@ -110,12 +110,17 @@ export const handler = obfiousHandler({
110
110
 
111
111
  ## CSP requirements
112
112
 
113
+ Obfious needs to compile WebAssembly and spawn a Web Worker from same-origin URLs. If your app sets an explicit `script-src` directive, you must include `'wasm-unsafe-eval'`:
114
+
113
115
  ```
114
116
  script-src 'self' 'wasm-unsafe-eval';
115
117
  worker-src 'self';
116
- connect-src 'self';
117
118
  ```
118
119
 
120
+ If you only have `default-src 'self'` with **no** explicit `script-src`, WASM compilation is implicitly allowed and no additional directives are needed. The `'wasm-unsafe-eval'` requirement only kicks in when `script-src` is explicitly set.
121
+
122
+ `worker-src` similarly falls back to `script-src` then `default-src` — only set it explicitly if your policy requires it.
123
+
119
124
  ## License
120
125
 
121
126
  See LICENSE file.
package/dist/express.js CHANGED
@@ -1 +1 @@
1
- var C="x-obfious-key",P="x-obfious-sig",T="x-obfious-ts",A=/\.(json|js|gif|png|woff2|css)$/;var p=class{config;creds=null;randomValue=null;randomValueCreatedAt=0;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;if(!this.creds)throw new Error("Credentials required for script URL derivation");let e=await w(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){if(!this.creds)throw new Error("Credentials required for worker URL derivation");let e=await m(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t,r){let o={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return o;let n=new URL(e.url);if(e.method==="GET"){if(n.pathname==="/"&&this.creds)for(let[l]of n.searchParams){if(await k(this.creds.secret,l)){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await S(this.creds.secret,l)){let u=await this.fetchWorker();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&n.pathname===this.config.scriptPath){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&A.test(n.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,n.pathname)};let u=e.clone(),g=new Uint8Array(await u.arrayBuffer());if(g.length>0&&g[0]===91)return{response:await this.forwardToApi(e,n.pathname,g)}}if(this.config.excludePaths?.some(l=>n.pathname.startsWith(l))||this.config.includePaths&&!this.config.includePaths.some(l=>n.pathname.startsWith(l)))return o;let i=e.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let a=i.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let c=i.slice(0,a),h=i.slice(a+1),d=U(c);if(!d)return{response:new Response(null,{status:401})};let x=r&&this.config.privateKey?await O(r,this.config.privateKey):void 0,y=await this.validateToken(d,c,h,x);return y.valid?{response:null,deviceId:y.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=E(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=this.creds?await this.getWorkerUrl():"",t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let o={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,i]of Object.entries(this.config.getPlatformSignals(e)))o[n.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:o,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[o,n]of Object.entries(this.config.getPlatformSignals(e)))r[o.replace(/[\r\n]/g,"")]=String(n).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,o){try{let n={tokenHex:e,signature:r,payload:t};o&&(n.encryptedUser=o);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!i.ok)return{valid:!1};let a=await i.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,o=Date.now().toString(),n=(t.method||"GET").toUpperCase(),i=`${o}.${n}.${e}`,a=await I(this.creds.secret,i),c=new Headers(t.headers);return c.set(C,this.creds.keyId),c.set(P,a),c.set(T,o),fetch(r,{...t,headers:c})}};async function w(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(o)).slice(0,10)}async function k(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await w(s,t)===e)return!0;return!1}async function m(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,o=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",o,new TextEncoder().encode("obfious-worker-v1:"+t)),i=f(new Uint8Array(n)).slice(0,9);return i.slice(0,r)+"w"+i.slice(r)}async function S(s,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let o of[-1,0,1])if(await m(s,o)===e)return!0;return!1}function f(s){return Array.from(s,e=>e.toString(16).padStart(2,"0")).join("")}async function I(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(r))}async function O(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(s));return f(new Uint8Array(r))}function U(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function E(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(s));return Array.from(t,r=>e[r%e.length]).join("")}import{Readable as b}from"node:stream";function R(s){let e=s.headers["x-forwarded-proto"]||"http",t=s.headers.host||"localhost",r=`${e}://${t}${s.url}`,o=new Headers;for(let[i,a]of Object.entries(s.headers))a&&o.set(i,Array.isArray(a)?a.join(", "):a);let n=s.method!=="GET"&&s.method!=="HEAD";return new Request(r,{method:s.method,headers:o,body:n?b.toWeb(b.from(s)):null,duplex:"half"})}async function v(s,e){let t={};if(e.headers.forEach((r,o)=>{t[o]=r}),s.writeHead(e.status,t),e.body){let r=e.body.getReader();try{for(;;){let{done:o,value:n}=await r.read();if(o)break;s.write(n)}}finally{r.releaseLock()}}s.end()}function W(s){let{creds:e,getUser:t,...r}=s,o=new p({...r,getClientIp:r.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown"),getPlatformSignals:r.getPlatformSignals??(()=>({}))});return async(n,i,a)=>{try{let c=R(n),h=t?.(n),d=await o.protect(c,e,h);if(d.response){await v(i,d.response);return}d.deviceId&&(n.obfiousDeviceId=d.deviceId),a()}catch(c){a(c)}}}export{p as Obfious,W as obfiousMiddleware};
1
+ var R="x-obfious-key",x="x-obfious-sig",C="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/;var p=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(e){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"},this.creds={keyId:e.keyId,secret:e.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){let e=await m(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t){let r=new URL(e.url);if(e.method==="GET"){if(r.pathname==="/")for(let[l]of r.searchParams){if(await T(this.creds.secret,l)){let d=await this.fetchBundle();if(d)return{response:new Response(d,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await A(this.creds.secret,l)){let d=await this.fetchWorker();if(d)return{response:new Response(d,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&r.pathname===this.config.scriptPath){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&P.test(r.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,r.pathname)};let d=e.clone(),h=new Uint8Array(await d.arrayBuffer());if(h.length>0&&h[0]===91)return{response:await this.forwardToApi(e,r.pathname,h)}}if(this.config.excludePaths?.some(l=>r.pathname.startsWith(l)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(l=>r.pathname.startsWith(l)))return{response:null};let s=e.headers.get("x-req-auth");if(!s)return{response:new Response(null,{status:401})};let o=s.indexOf(".");if(o<1)return{response:new Response(null,{status:401})};let i=s.slice(0,o),a=s.slice(o+1),c=S(i);if(!c)return{response:new Response(null,{status:401})};let g=t&&this.config.privateKey?await I(t,this.config.privateKey):void 0,u=await this.validateToken(c,i,a,g);return u.valid?{response:null,deviceId:u.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=O(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.getWorkerUrl(),t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[o,i]of Object.entries(this.config.getPlatformSignals(e)))s[o.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:s,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[s,o]of Object.entries(this.config.getPlatformSignals(e)))r[s.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,s){try{let o={tokenHex:e,signature:r,payload:t};s&&(o.encryptedUser=s);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!i.ok)return{valid:!1};let a=await i.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,s=Date.now().toString(),o=(t.method||"GET").toUpperCase(),i=`${s}.${o}.${e}`,a=await k(this.creds.secret,i),c=new Headers(t.headers);return c.set(R,this.creds.keyId),c.set(x,a),c.set(C,s),fetch(r,{...t,headers:c})}};async function y(n,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(s)).slice(0,10)}async function T(n,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await y(n,t)===e)return!0;return!1}async function m(n,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-worker-v1:"+t)),i=f(new Uint8Array(o)).slice(0,9);return i.slice(0,r)+"w"+i.slice(r)}async function A(n,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let s of[-1,0,1])if(await m(n,s)===e)return!0;return!1}function f(n){return Array.from(n,e=>e.toString(16).padStart(2,"0")).join("")}async function k(n,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(r))}async function I(n,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(n));return f(new Uint8Array(r))}function S(n){try{let e=n.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function O(n){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(n));return Array.from(t,r=>e[r%e.length]).join("")}import{Readable as w}from"node:stream";function b(n){let e=n.headers["x-forwarded-proto"]||"http",t=n.headers.host||"localhost",r=`${e}://${t}${n.url}`,s=new Headers;for(let[i,a]of Object.entries(n.headers))a&&s.set(i,Array.isArray(a)?a.join(", "):a);let o=n.method!=="GET"&&n.method!=="HEAD";return new Request(r,{method:n.method,headers:s,body:o?w.toWeb(w.from(n)):null,duplex:"half"})}async function v(n,e){let t={};if(e.headers.forEach((r,s)=>{t[s]=r}),n.writeHead(e.status,t),e.body){let r=e.body.getReader();try{for(;;){let{done:s,value:o}=await r.read();if(s)break;n.write(o)}}finally{r.releaseLock()}}n.end()}function V(n){let{creds:e,getUser:t,...r}=n,s=new p({...r,getClientIp:r.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:r.getPlatformSignals??(()=>({}))});return async(o,i,a)=>{try{let c=b(o),g=t?.(o),u=await s.protect(c,e,g);if(u.response){await v(i,u.response);return}u.deviceId&&(o.obfiousDeviceId=u.deviceId),a()}catch(c){a(c)}}}export{p as Obfious,V as obfiousMiddleware};
package/dist/fastify.js CHANGED
@@ -1 +1 @@
1
- var x="x-obfious-key",C="x-obfious-sig",P="x-obfious-ts",T=/\.(json|js|gif|png|woff2|css)$/;var g=class{config;creds=null;randomValue=null;randomValueCreatedAt=0;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;if(!this.creds)throw new Error("Credentials required for script URL derivation");let e=await m(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){if(!this.creds)throw new Error("Credentials required for worker URL derivation");let e=await b(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t,r){let s={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return s;let o=new URL(e.url);if(e.method==="GET"){if(o.pathname==="/"&&this.creds)for(let[c]of o.searchParams){if(await A(this.creds.secret,c)){let f=await this.fetchBundle();if(f)return{response:new Response(f,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await k(this.creds.secret,c)){let f=await this.fetchWorker();if(f)return{response:new Response(f,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&o.pathname===this.config.scriptPath){let c=await this.fetchBundle();if(c)return{response:new Response(c,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&T.test(o.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,o.pathname)};let f=e.clone(),w=new Uint8Array(await f.arrayBuffer());if(w.length>0&&w[0]===91)return{response:await this.forwardToApi(e,o.pathname,w)}}if(this.config.excludePaths?.some(c=>o.pathname.startsWith(c))||this.config.includePaths&&!this.config.includePaths.some(c=>o.pathname.startsWith(c)))return s;let i=e.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let a=i.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let l=i.slice(0,a),y=i.slice(a+1),d=O(l);if(!d)return{response:new Response(null,{status:401})};let p=r&&this.config.privateKey?await I(r,this.config.privateKey):void 0,h=await this.validateToken(d,l,y,p);return h.valid?{response:null,deviceId:h.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=U(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=this.creds?await this.getWorkerUrl():"",t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[o,i]of Object.entries(this.config.getPlatformSignals(e)))s[o.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:s,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[s,o]of Object.entries(this.config.getPlatformSignals(e)))r[s.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,s){try{let o={tokenHex:e,signature:r,payload:t};s&&(o.encryptedUser=s);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!i.ok)return{valid:!1};let a=await i.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,s=Date.now().toString(),o=(t.method||"GET").toUpperCase(),i=`${s}.${o}.${e}`,a=await S(this.creds.secret,i),l=new Headers(t.headers);return l.set(x,this.creds.keyId),l.set(C,a),l.set(P,s),fetch(r,{...t,headers:l})}};async function m(n,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return u(new Uint8Array(s)).slice(0,10)}async function A(n,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await m(n,t)===e)return!0;return!1}async function b(n,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-worker-v1:"+t)),i=u(new Uint8Array(o)).slice(0,9);return i.slice(0,r)+"w"+i.slice(r)}async function k(n,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let s of[-1,0,1])if(await b(n,s)===e)return!0;return!1}function u(n){return Array.from(n,e=>e.toString(16).padStart(2,"0")).join("")}async function S(n,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return u(new Uint8Array(r))}async function I(n,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(n));return u(new Uint8Array(r))}function O(n){try{let e=n.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:u(t.slice(1,9))}catch{return null}}function U(n){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(n));return Array.from(t,r=>e[r%e.length]).join("")}import{Readable as R}from"node:stream";function v(n){let e=n.headers["x-forwarded-proto"]||"http",t=n.headers.host||"localhost",r=`${e}://${t}${n.url}`,s=new Headers;for(let[i,a]of Object.entries(n.headers))a&&s.set(i,Array.isArray(a)?a.join(", "):a);let o=n.method!=="GET"&&n.method!=="HEAD";return new Request(r,{method:n.method,headers:s,body:o?R.toWeb(R.from(n)):null,duplex:"half"})}async function j(n,e){let{creds:t,getUser:r,...s}=e,o=new g({...s,getClientIp:s.getClientIp??(i=>i.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||i.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});n.addHook("onRequest",async(i,a)=>{let l=v(i.raw),y=r?.(i.raw),d=await o.protect(l,t,y);if(d.response){let p={};d.response.headers.forEach((c,f)=>{p[f]=c});let h=await d.response.text();a.code(d.response.status).headers(p).send(h);return}d.deviceId&&(i.obfiousDeviceId=d.deviceId)})}export{g as Obfious,j as obfiousPlugin};
1
+ var v="x-obfious-key",x="x-obfious-sig",P="x-obfious-ts",C=/\.(json|js|gif|png|woff2|css)$/;var g=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(e){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"},this.creds={keyId:e.keyId,secret:e.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){let e=await m(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),n=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${n}></script>`}async protect(e,t){let n=new URL(e.url);if(e.method==="GET"){if(n.pathname==="/")for(let[c]of n.searchParams){if(await T(this.creds.secret,c)){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await A(this.creds.secret,c)){let u=await this.fetchWorker();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&n.pathname===this.config.scriptPath){let c=await this.fetchBundle();if(c)return{response:new Response(c,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&C.test(n.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,n.pathname)};let u=e.clone(),f=new Uint8Array(await u.arrayBuffer());if(f.length>0&&f[0]===91)return{response:await this.forwardToApi(e,n.pathname,f)}}if(this.config.excludePaths?.some(c=>n.pathname.startsWith(c)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(c=>n.pathname.startsWith(c)))return{response:null};let s=e.headers.get("x-req-auth");if(!s)return{response:new Response(null,{status:401})};let i=s.indexOf(".");if(i<1)return{response:new Response(null,{status:401})};let o=s.slice(0,i),a=s.slice(i+1),l=S(o);if(!l)return{response:new Response(null,{status:401})};let h=t&&this.config.privateKey?await I(t,this.config.privateKey):void 0,d=await this.validateToken(l,o,a,h);return d.valid?{response:null,deviceId:d.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=O(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.getWorkerUrl(),t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,n){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))s[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:s,body:n.buffer})}async forwardStreamToApi(e,t){let n={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[s,i]of Object.entries(this.config.getPlatformSignals(e)))n[s.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:e.body??void 0})}async validateToken(e,t,n,s){try{let i={tokenHex:e,signature:n,payload:t};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let a=await o.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let n=`${this.config.apiUrl}${e}`,s=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${s}.${i}.${e}`,a=await k(this.creds.secret,o),l=new Headers(t.headers);return l.set(v,this.creds.keyId),l.set(x,a),l.set(P,s),fetch(n,{...t,headers:l})}};async function y(r,e=0){let t=Math.floor(Date.now()/3e5)+e,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return p(new Uint8Array(s)).slice(0,10)}async function T(r,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await y(r,t)===e)return!0;return!1}async function m(r,e=0){let t=Math.floor(Date.now()/3e5)+e,n=t%7+1,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-worker-v1:"+t)),o=p(new Uint8Array(i)).slice(0,9);return o.slice(0,n)+"w"+o.slice(n)}async function A(r,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let n=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(n))return!1;for(let s of[-1,0,1])if(await m(r,s)===e)return!0;return!1}function p(r){return Array.from(r,e=>e.toString(16).padStart(2,"0")).join("")}async function k(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return p(new Uint8Array(n))}async function I(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(r));return p(new Uint8Array(n))}function S(r){try{let e=r.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),n=>n.charCodeAt(0));return t.length<9||t[0]!==33?null:p(t.slice(1,9))}catch{return null}}function O(r){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(r));return Array.from(t,n=>e[n%e.length]).join("")}import{Readable as w}from"node:stream";function b(r){let e=r.headers["x-forwarded-proto"]||"http",t=r.headers.host||"localhost",n=`${e}://${t}${r.url}`,s=new Headers;for(let[o,a]of Object.entries(r.headers))a&&s.set(o,Array.isArray(a)?a.join(", "):a);let i=r.method!=="GET"&&r.method!=="HEAD";return new Request(n,{method:r.method,headers:s,body:i?w.toWeb(w.from(r)):null,duplex:"half"})}async function V(r,e){let{creds:t,getUser:n,...s}=e,i=new g({...s,getClientIp:s.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});r.addHook("onRequest",async(o,a)=>{let l=b(o.raw),h=n?.(o.raw),d=await i.protect(l,t,h);if(d.response){let c={};d.response.headers.forEach((f,R)=>{c[R]=f});let u=await d.response.text();a.code(d.response.status).headers(c).send(u);return}d.deviceId&&(o.obfiousDeviceId=d.deviceId)})}export{g as Obfious,V as obfiousPlugin};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var v="x-obfious-key",C="x-obfious-sig",P="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/;var d=class{config;creds=null;randomValue=null;randomValueCreatedAt=0;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;if(!this.creds)throw new Error("Credentials required for script URL derivation");let e=await w(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){if(!this.creds)throw new Error("Credentials required for worker URL derivation");let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t,r){let n={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return n;let s=new URL(e.url);if(e.method==="GET"){if(s.pathname==="/"&&this.creds)for(let[a]of s.searchParams){if(await T(this.creds.secret,a)){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await x(this.creds.secret,a)){let u=await this.fetchWorker();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&s.pathname===this.config.scriptPath){let a=await this.fetchBundle();if(a)return{response:new Response(a,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&R.test(s.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,s.pathname)};let u=e.clone(),h=new Uint8Array(await u.arrayBuffer());if(h.length>0&&h[0]===91)return{response:await this.forwardToApi(e,s.pathname,h)}}if(this.config.excludePaths?.some(a=>s.pathname.startsWith(a))||this.config.includePaths&&!this.config.includePaths.some(a=>s.pathname.startsWith(a)))return n;let o=e.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let c=o.indexOf(".");if(c<1)return{response:new Response(null,{status:401})};let l=o.slice(0,c),m=o.slice(c+1),p=S(l);if(!p)return{response:new Response(null,{status:401})};let b=r&&this.config.privateKey?await k(r,this.config.privateKey):void 0,g=await this.validateToken(p,l,m,b);return g.valid?{response:null,deviceId:g.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=U(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=this.creds?await this.getWorkerUrl():"",t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[s,o]of Object.entries(this.config.getPlatformSignals(e)))n[s.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,s]of Object.entries(this.config.getPlatformSignals(e)))r[n.replace(/[\r\n]/g,"")]=String(s).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,n){try{let s={tokenHex:e,signature:r,payload:t};n&&(s.encryptedUser=n);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!o.ok)return{valid:!1};let c=await o.json();return{valid:c.valid===!0,deviceId:c.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,n=Date.now().toString(),s=(t.method||"GET").toUpperCase(),o=`${n}.${s}.${e}`,c=await A(this.creds.secret,o),l=new Headers(t.headers);return l.set(v,this.creds.keyId),l.set(C,c),l.set(P,n),fetch(r,{...t,headers:l})}};async function w(i,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(i),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(n)).slice(0,10)}async function T(i,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await w(i,t)===e)return!0;return!1}async function y(i,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(i),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-worker-v1:"+t)),o=f(new Uint8Array(s)).slice(0,9);return o.slice(0,r)+"w"+o.slice(r)}async function x(i,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let n of[-1,0,1])if(await y(i,n)===e)return!0;return!1}function f(i){return Array.from(i,e=>e.toString(16).padStart(2,"0")).join("")}async function A(i,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(i),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(r))}async function k(i,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(i));return f(new Uint8Array(r))}function S(i){try{let e=i.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function U(i){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(i));return Array.from(t,r=>e[r%e.length]).join("")}export{d as Obfious};
1
+ var m="x-obfious-key",b="x-obfious-sig",v="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/;var h=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(e){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"},this.creds={keyId:e.keyId,secret:e.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let e=await d(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),n=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${n}></script>`}async protect(e,t){let n=new URL(e.url);if(e.method==="GET"){if(n.pathname==="/")for(let[a]of n.searchParams){if(await T(this.creds.secret,a)){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await C(this.creds.secret,a)){let l=await this.fetchWorker();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&n.pathname===this.config.scriptPath){let a=await this.fetchBundle();if(a)return{response:new Response(a,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&P.test(n.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,n.pathname)};let l=e.clone(),p=new Uint8Array(await l.arrayBuffer());if(p.length>0&&p[0]===91)return{response:await this.forwardToApi(e,n.pathname,p)}}if(this.config.excludePaths?.some(a=>n.pathname.startsWith(a)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(a=>n.pathname.startsWith(a)))return{response:null};let r=e.headers.get("x-req-auth");if(!r)return{response:new Response(null,{status:401})};let i=r.indexOf(".");if(i<1)return{response:new Response(null,{status:401})};let o=r.slice(0,i),u=r.slice(i+1),c=R(o);if(!c)return{response:new Response(null,{status:401})};let w=t&&this.config.privateKey?await A(t,this.config.privateKey):void 0,g=await this.validateToken(c,o,u,w);return g.valid?{response:null,deviceId:g.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=k(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.getWorkerUrl(),t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,n){let r={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))r[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:n.buffer})}async forwardStreamToApi(e,t){let n={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[r,i]of Object.entries(this.config.getPlatformSignals(e)))n[r.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:e.body??void 0})}async validateToken(e,t,n,r){try{let i={tokenHex:e,signature:n,payload:t};r&&(i.encryptedUser=r);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let u=await o.json();return{valid:u.valid===!0,deviceId:u.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let n=`${this.config.apiUrl}${e}`,r=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${r}.${i}.${e}`,u=await x(this.creds.secret,o),c=new Headers(t.headers);return c.set(m,this.creds.keyId),c.set(b,u),c.set(v,r),fetch(n,{...t,headers:c})}};async function d(s,e=0){let t=Math.floor(Date.now()/3e5)+e,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(r)).slice(0,10)}async function T(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await d(s,t)===e)return!0;return!1}async function y(s,e=0){let t=Math.floor(Date.now()/3e5)+e,n=t%7+1,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-worker-v1:"+t)),o=f(new Uint8Array(i)).slice(0,9);return o.slice(0,n)+"w"+o.slice(n)}async function C(s,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let n=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(n))return!1;for(let r of[-1,0,1])if(await y(s,r)===e)return!0;return!1}function f(s){return Array.from(s,e=>e.toString(16).padStart(2,"0")).join("")}async function x(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(n))}async function A(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(s));return f(new Uint8Array(n))}function R(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),n=>n.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function k(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(s));return Array.from(t,n=>e[n%e.length]).join("")}export{h as Obfious};
package/dist/lambda.js CHANGED
@@ -1 +1 @@
1
- var P="x-obfious-key",x="x-obfious-sig",R="x-obfious-ts",C=/\.(json|js|gif|png|woff2|css)$/;var g=class{config;creds=null;randomValue=null;randomValueCreatedAt=0;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;if(!this.creds)throw new Error("Credentials required for script URL derivation");let e=await m(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){if(!this.creds)throw new Error("Credentials required for worker URL derivation");let e=await b(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),s=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${s}></script>`}async protect(e,t,s){let n={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return n;let i=new URL(e.url);if(e.method==="GET"){if(i.pathname==="/"&&this.creds)for(let[d]of i.searchParams){if(await A(this.creds.secret,d)){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await T(this.creds.secret,d)){let l=await this.fetchWorker();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&i.pathname===this.config.scriptPath){let d=await this.fetchBundle();if(d)return{response:new Response(d,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&C.test(i.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,i.pathname)};let l=e.clone(),y=new Uint8Array(await l.arrayBuffer());if(y.length>0&&y[0]===91)return{response:await this.forwardToApi(e,i.pathname,y)}}if(this.config.excludePaths?.some(d=>i.pathname.startsWith(d))||this.config.includePaths&&!this.config.includePaths.some(d=>i.pathname.startsWith(d)))return n;let o=e.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let c=o.indexOf(".");if(c<1)return{response:new Response(null,{status:401})};let a=o.slice(0,c),h=o.slice(c+1),u=I(a);if(!u)return{response:new Response(null,{status:401})};let f=s&&this.config.privateKey?await v(s,this.config.privateKey):void 0,w=await this.validateToken(u,a,h,f);return w.valid?{response:null,deviceId:w.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=S(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=this.creds?await this.getWorkerUrl():"",t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,s){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))n[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:s.buffer})}async forwardStreamToApi(e,t){let s={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,i]of Object.entries(this.config.getPlatformSignals(e)))s[n.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:s,body:e.body??void 0})}async validateToken(e,t,s,n){try{let i={tokenHex:e,signature:s,payload:t};n&&(i.encryptedUser=n);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let c=await o.json();return{valid:c.valid===!0,deviceId:c.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let s=`${this.config.apiUrl}${e}`,n=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${n}.${i}.${e}`,c=await k(this.creds.secret,o),a=new Headers(t.headers);return a.set(P,this.creds.keyId),a.set(x,c),a.set(R,n),fetch(s,{...t,headers:a})}};async function m(r,e=0){let t=Math.floor(Date.now()/3e5)+e,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return p(new Uint8Array(n)).slice(0,10)}async function A(r,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await m(r,t)===e)return!0;return!1}async function b(r,e=0){let t=Math.floor(Date.now()/3e5)+e,s=t%7+1,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-worker-v1:"+t)),o=p(new Uint8Array(i)).slice(0,9);return o.slice(0,s)+"w"+o.slice(s)}async function T(r,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let s=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(s))return!1;for(let n of[-1,0,1])if(await b(r,n)===e)return!0;return!1}function p(r){return Array.from(r,e=>e.toString(16).padStart(2,"0")).join("")}async function k(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return p(new Uint8Array(s))}async function v(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(r));return p(new Uint8Array(s))}function I(r){try{let e=r.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),s=>s.charCodeAt(0));return t.length<9||t[0]!==33?null:p(t.slice(1,9))}catch{return null}}function S(r){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(r));return Array.from(t,s=>e[s%e.length]).join("")}function E(r){let e=r.headers["x-forwarded-proto"]||r.headers["X-Forwarded-Proto"]||"https",t=r.headers.host||r.headers.Host||"localhost",s=`${e}://${t}${r.path}`;if(r.queryStringParameters){let c=new URLSearchParams;for(let[h,u]of Object.entries(r.queryStringParameters))u!=null&&c.set(h,u);let a=c.toString();a&&(s+=`?${a}`)}let n=new Headers;for(let[c,a]of Object.entries(r.headers))a&&n.set(c,a);let o=r.httpMethod!=="GET"&&r.httpMethod!=="HEAD"&&r.body!=null?r.isBase64Encoded?atob(r.body):r.body:null;return new Request(s,{method:r.httpMethod,headers:n,body:o})}async function O(r){let e={};return r.headers.forEach((t,s)=>{e[s]=t}),{statusCode:r.status,headers:e,body:await r.text()}}function $(r,e){let{creds:t,getUser:s,...n}=r,i=new g({...n,getClientIp:n.getClientIp??(o=>o.headers.get("x-lambda-source-ip")||o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});return async(o,c)=>{let a=E(o),h=o.requestContext?.identity?.sourceIp||o.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";a.headers.set("x-lambda-source-ip",h);let u=s?.(o),f=await i.protect(a,t,u);return f.response?O(f.response):(f.deviceId&&(o.headers["x-obfious-device-id"]=String(f.deviceId)),e(o,c))}}export{g as Obfious,$ as obfiousHandler};
1
+ var m="x-obfious-key",b="x-obfious-sig",P="x-obfious-ts",x=/\.(json|js|gif|png|woff2|css)$/;var g=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(e){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"},this.creds={keyId:e.keyId,secret:e.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){let e=await w(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t){let r=new URL(e.url);if(e.method==="GET"){if(r.pathname==="/")for(let[c]of r.searchParams){if(await R(this.creds.secret,c)){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await C(this.creds.secret,c)){let l=await this.fetchWorker();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&r.pathname===this.config.scriptPath){let c=await this.fetchBundle();if(c)return{response:new Response(c,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&x.test(r.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,r.pathname)};let l=e.clone(),h=new Uint8Array(await l.arrayBuffer());if(h.length>0&&h[0]===91)return{response:await this.forwardToApi(e,r.pathname,h)}}if(this.config.excludePaths?.some(c=>r.pathname.startsWith(c)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(c=>r.pathname.startsWith(c)))return{response:null};let n=e.headers.get("x-req-auth");if(!n)return{response:new Response(null,{status:401})};let i=n.indexOf(".");if(i<1)return{response:new Response(null,{status:401})};let o=n.slice(0,i),d=n.slice(i+1),a=k(o);if(!a)return{response:new Response(null,{status:401})};let f=t&&this.config.privateKey?await T(t,this.config.privateKey):void 0,u=await this.validateToken(a,o,d,f);return u.valid?{response:null,deviceId:u.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=I(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.getWorkerUrl(),t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))n[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,i]of Object.entries(this.config.getPlatformSignals(e)))r[n.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,n){try{let i={tokenHex:e,signature:r,payload:t};n&&(i.encryptedUser=n);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let d=await o.json();return{valid:d.valid===!0,deviceId:d.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,n=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${n}.${i}.${e}`,d=await A(this.creds.secret,o),a=new Headers(t.headers);return a.set(m,this.creds.keyId),a.set(b,d),a.set(P,n),fetch(r,{...t,headers:a})}};async function y(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return p(new Uint8Array(n)).slice(0,10)}async function R(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await y(s,t)===e)return!0;return!1}async function w(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-worker-v1:"+t)),o=p(new Uint8Array(i)).slice(0,9);return o.slice(0,r)+"w"+o.slice(r)}async function C(s,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let n of[-1,0,1])if(await w(s,n)===e)return!0;return!1}function p(s){return Array.from(s,e=>e.toString(16).padStart(2,"0")).join("")}async function A(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return p(new Uint8Array(r))}async function T(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(s));return p(new Uint8Array(r))}function k(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:p(t.slice(1,9))}catch{return null}}function I(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(s));return Array.from(t,r=>e[r%e.length]).join("")}function v(s){let e=s.headers["x-forwarded-proto"]||s.headers["X-Forwarded-Proto"]||"https",t=s.headers.host||s.headers.Host||"localhost",r=`${e}://${t}${s.path}`;if(s.queryStringParameters){let d=new URLSearchParams;for(let[f,u]of Object.entries(s.queryStringParameters))u!=null&&d.set(f,u);let a=d.toString();a&&(r+=`?${a}`)}let n=new Headers;for(let[d,a]of Object.entries(s.headers))a&&n.set(d,a);let o=s.httpMethod!=="GET"&&s.httpMethod!=="HEAD"&&s.body!=null?s.isBase64Encoded?atob(s.body):s.body:null;return new Request(r,{method:s.httpMethod,headers:n,body:o})}async function S(s){let e={};return s.headers.forEach((t,r)=>{e[r]=t}),{statusCode:s.status,headers:e,body:await s.text()}}function H(s,e){let{creds:t,getUser:r,...n}=s,i=new g({...n,getClientIp:n.getClientIp??(o=>o.headers.get("x-lambda-source-ip")||o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});return async(o,d)=>{let a=v(o),f=o.requestContext?.identity?.sourceIp||o.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";a.headers.set("x-lambda-source-ip",f);let u=r?.(o),c=await i.protect(a,t,u);return c.response?S(c.response):(c.deviceId&&(o.headers["x-obfious-device-id"]=String(c.deviceId)),e(o,d))}}export{g as Obfious,H as obfiousHandler};
package/dist/nextjs.js CHANGED
@@ -1 +1 @@
1
- var C="x-obfious-key",P="x-obfious-sig",R="x-obfious-ts",v=/\.(json|js|gif|png|woff2|css)$/;var d=class{config;creds=null;randomValue=null;randomValueCreatedAt=0;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;if(!this.creds)throw new Error("Credentials required for script URL derivation");let e=await w(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){if(!this.creds)throw new Error("Credentials required for worker URL derivation");let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),r=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${r}></script>`}async protect(e,t,r){let n={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return n;let i=new URL(e.url);if(e.method==="GET"){if(i.pathname==="/"&&this.creds)for(let[a]of i.searchParams){if(await x(this.creds.secret,a)){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await T(this.creds.secret,a)){let u=await this.fetchWorker();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&i.pathname===this.config.scriptPath){let a=await this.fetchBundle();if(a)return{response:new Response(a,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&v.test(i.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,i.pathname)};let u=e.clone(),p=new Uint8Array(await u.arrayBuffer());if(p.length>0&&p[0]===91)return{response:await this.forwardToApi(e,i.pathname,p)}}if(this.config.excludePaths?.some(a=>i.pathname.startsWith(a))||this.config.includePaths&&!this.config.includePaths.some(a=>i.pathname.startsWith(a)))return n;let o=e.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let c=o.indexOf(".");if(c<1)return{response:new Response(null,{status:401})};let l=o.slice(0,c),m=o.slice(c+1),g=O(l);if(!g)return{response:new Response(null,{status:401})};let b=r&&this.config.privateKey?await k(r,this.config.privateKey):void 0,h=await this.validateToken(g,l,m,b);return h.valid?{response:null,deviceId:h.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=S(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=this.creds?await this.getWorkerUrl():"",t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,r){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))n[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:r.buffer})}async forwardStreamToApi(e,t){let r={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,i]of Object.entries(this.config.getPlatformSignals(e)))r[n.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:r,body:e.body??void 0})}async validateToken(e,t,r,n){try{let i={tokenHex:e,signature:r,payload:t};n&&(i.encryptedUser=n);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let c=await o.json();return{valid:c.valid===!0,deviceId:c.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,n=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${n}.${i}.${e}`,c=await A(this.creds.secret,o),l=new Headers(t.headers);return l.set(C,this.creds.keyId),l.set(P,c),l.set(R,n),fetch(r,{...t,headers:l})}};async function w(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(n)).slice(0,10)}async function x(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await w(s,t)===e)return!0;return!1}async function y(s,e=0){let t=Math.floor(Date.now()/3e5)+e,r=t%7+1,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-worker-v1:"+t)),o=f(new Uint8Array(i)).slice(0,9);return o.slice(0,r)+"w"+o.slice(r)}async function T(s,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let r=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(r))return!1;for(let n of[-1,0,1])if(await y(s,n)===e)return!0;return!1}function f(s){return Array.from(s,e=>e.toString(16).padStart(2,"0")).join("")}async function A(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(r))}async function k(s,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(s));return f(new Uint8Array(r))}function O(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),r=>r.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function S(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(s));return Array.from(t,r=>e[r%e.length]).join("")}function E(s){let{creds:e,...t}=s,r=new d({...t,getClientIp:t.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown")});return async n=>(await r.protect(n,e)).response}async function H(s,e){return s.scriptTag({nonce:e})}export{d as Obfious,E as createObfiousMiddleware,H as obfiousScriptTag};
1
+ var m="x-obfious-key",b="x-obfious-sig",P="x-obfious-ts",T=/\.(json|js|gif|png|woff2|css)$/;var p=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(e){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"},this.creds={keyId:e.keyId,secret:e.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let e=await h(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async getWorkerUrl(){let e=await y(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let t=await this.getScriptUrl(),n=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${n}></script>`}async protect(e,t){let n=new URL(e.url);if(e.method==="GET"){if(n.pathname==="/")for(let[a]of n.searchParams){if(await v(this.creds.secret,a)){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}if(await x(this.creds.secret,a)){let l=await this.fetchWorker();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"private, max-age=300"}})};break}}if(this.config.scriptPath&&n.pathname===this.config.scriptPath){let a=await this.fetchBundle();if(a)return{response:new Response(a,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&T.test(n.pathname)){if((e.headers.get("Content-Type")||"")==="application/octet-stream")return{response:await this.forwardStreamToApi(e,n.pathname)};let l=e.clone(),g=new Uint8Array(await l.arrayBuffer());if(g.length>0&&g[0]===91)return{response:await this.forwardToApi(e,n.pathname,g)}}if(this.config.excludePaths?.some(a=>n.pathname.startsWith(a)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(a=>n.pathname.startsWith(a)))return{response:null};let s=e.headers.get("x-req-auth");if(!s)return{response:new Response(null,{status:401})};let i=s.indexOf(".");if(i<1)return{response:new Response(null,{status:401})};let o=s.slice(0,i),u=s.slice(i+1),c=A(o);if(!c)return{response:new Response(null,{status:401})};let w=t&&this.config.privateKey?await R(t,this.config.privateKey):void 0,d=await this.validateToken(c,o,u,w);return d.valid?{response:null,deviceId:d.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=k(8),this.randomValueCreatedAt=Date.now())}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.getWorkerUrl(),t=await this.authedFetch("/b",{method:"GET",headers:{"x-obfious-worker-url":e}});return t.ok?await t.text():null}catch{return null}}async fetchWorker(){try{let e=await this.authedFetch("/w",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t,n){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,o]of Object.entries(this.config.getPlatformSignals(e)))s[i.replace(/[\r\n]/g,"")]=String(o).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:s,body:n.buffer})}async forwardStreamToApi(e,t){let n={"Content-Type":"application/octet-stream","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[s,i]of Object.entries(this.config.getPlatformSignals(e)))n[s.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");return this.authedFetch(t,{method:"POST",headers:n,body:e.body??void 0})}async validateToken(e,t,n,s){try{let i={tokenHex:e,signature:n,payload:t};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let u=await o.json();return{valid:u.valid===!0,deviceId:u.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let n=`${this.config.apiUrl}${e}`,s=Date.now().toString(),i=(t.method||"GET").toUpperCase(),o=`${s}.${i}.${e}`,u=await C(this.creds.secret,o),c=new Headers(t.headers);return c.set(m,this.creds.keyId),c.set(b,u),c.set(P,s),fetch(n,{...t,headers:c})}};async function h(r,e=0){let t=Math.floor(Date.now()/3e5)+e,n=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return f(new Uint8Array(s)).slice(0,10)}async function v(r,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await h(r,t)===e)return!0;return!1}async function y(r,e=0){let t=Math.floor(Date.now()/3e5)+e,n=t%7+1,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-worker-v1:"+t)),o=f(new Uint8Array(i)).slice(0,9);return o.slice(0,n)+"w"+o.slice(n)}async function x(r,e){if(e.length!==10)return!1;let t=e.indexOf("w");if(t<1||t>7||e.lastIndexOf("w")!==t)return!1;let n=e.slice(0,t)+e.slice(t+1);if(!/^[0-9a-f]{9}$/.test(n))return!1;for(let s of[-1,0,1])if(await y(r,s)===e)return!0;return!1}function f(r){return Array.from(r,e=>e.toString(16).padStart(2,"0")).join("")}async function C(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return f(new Uint8Array(n))}async function R(r,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(r));return f(new Uint8Array(n))}function A(r){try{let e=r.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),n=>n.charCodeAt(0));return t.length<9||t[0]!==33?null:f(t.slice(1,9))}catch{return null}}function k(r){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(r));return Array.from(t,n=>e[n%e.length]).join("")}function U(r){let e=new p({...r,getClientIp:r.getClientIp??(t=>t.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||t.headers.get("x-real-ip")||"unknown")});return async t=>(await e.protect(t)).response}async function O(r,e){return r.scriptTag({nonce:e})}export{p as Obfious,U as createObfiousMiddleware,O as obfiousScriptTag};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obfious/js",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Obfious anti-bot protection for JavaScript — CF Workers, Next.js, Express, Fastify, Lambda",
5
5
  "type": "module",
6
6
  "exports": {