@obfious/js 0.1.14 → 0.1.17

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/dist/express.js CHANGED
@@ -1 +1 @@
1
- var b="x-obfious-key",v="x-obfious-sig",R="x-obfious-ts",x=/\.(json|js|gif|png|woff2|css)$/;var f=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 scriptTag(e){let r=await this.getScriptUrl(),t=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${r}"${t}></script>`}async protect(e,r){let t=new URL(e.url);if(e.method==="GET"){if(t.pathname==="/"){for(let[d]of t.searchParams)if(await C(this.creds.secret,d)){let u=await this.fetchBundle();return{response:new Response(u??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":u?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&t.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"&&x.test(t.pathname)){let d=e.clone(),u=new Uint8Array(await d.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(e,t.pathname,u)}}if(this.config.excludePaths?.some(d=>t.pathname.startsWith(d)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(d=>t.pathname.startsWith(d)))return{response:null};let o=e.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let n=o.indexOf(".");if(n<1)return{response:new Response(null,{status:401})};let a=o.slice(0,n),i=o.slice(n+1),c=I(a);if(!c)return{response:new Response(null,{status:401})};let g=r&&this.config.privateKey?await P(r,this.config.privateKey):void 0,l=await this.validateToken(c,a,i,g);return l.valid?{response:null,deviceId:l.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=T(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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,r,t){let o={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[a,i]of Object.entries(this.config.getPlatformSignals(e)))o[a.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");let n=await this.authedFetch(r,{method:"POST",headers:o,body:t.buffer});if(!n.ok){let a=await n.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${r}: ${n.status} ${a}`)}return n}async validateToken(e,r,t,o){try{let n={tokenHex:e,signature:t,payload:r};o&&(n.encryptedUser=o);let a=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok){let c=await a.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${a.status} ${c}`),{valid:!1}}let i=await a.json();return i.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(i)}`),{valid:i.valid===!0,deviceId:i.deviceId}}catch(n){return console.error("[obfious] Validate error:",n),{valid:!1}}}async authedFetch(e,r){let t=`${this.config.apiUrl}${e}`,o=Date.now().toString(),n=(r.method||"GET").toUpperCase(),a=`${o}.${n}.${e}`,i=await A(this.creds.secret,a),c=new Headers(r.headers);return c.set(b,this.creds.keyId),c.set(v,i),c.set(R,o),fetch(t,{...r,headers:c})}};async function h(s,e=0){let r=Math.floor(Date.now()/3e5)+e,t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode("obfious-bootstrap-v1:"+r));return p(new Uint8Array(o)).slice(0,10)}async function C(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let r of[-1,0,1])if(await h(s,r)===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 r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(e));return p(new Uint8Array(t))}async function P(s,e){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(s));return p(new Uint8Array(t))}function I(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let r=Uint8Array.from(atob(e),t=>t.charCodeAt(0));return r.length<9||r[0]!==33?null:p(r.slice(1,9))}catch{return null}}function T(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",r=crypto.getRandomValues(new Uint8Array(s));return Array.from(r,t=>e[t%e.length]).join("")}import{Readable as y}from"node:stream";function m(s){let e=s.headers["x-forwarded-proto"]||"http",r=s.headers.host||"localhost",t=`${e}://${r}${s.url}`,o=new Headers;for(let[a,i]of Object.entries(s.headers))i&&o.set(a,Array.isArray(i)?i.join(", "):i);let n=s.method!=="GET"&&s.method!=="HEAD";return new Request(t,{method:s.method,headers:o,body:n?y.toWeb(y.from(s)):null,duplex:"half"})}async function w(s,e){let r={};if(e.headers.forEach((t,o)=>{r[o]=t}),s.writeHead(e.status,r),e.body){let t=e.body.getReader();try{for(;;){let{done:o,value:n}=await t.read();if(o)break;s.write(n)}}finally{t.releaseLock()}}s.end()}function U(s){let{creds:e,getUser:r,...t}=s,o=new f({...t,keyId:e.keyId,secret:e.secret,getClientIp:t.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown"),getPlatformSignals:t.getPlatformSignals??(()=>({}))});return async(n,a,i)=>{try{let c=m(n),g=r?.(n),l=await o.protect(c,g);if(l.response){await w(a,l.response);return}l.deviceId&&(n.obfiousDeviceId=l.deviceId),i()}catch(c){i(c)}}}export{f as Obfious,U as obfiousMiddleware};
1
+ var x="x-obfious-key",P="x-obfious-sig",A="x-obfious-ts",C=/\.(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 w(this.creds.secret);return(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=await $(this.creds.secret,e),this.randomValueCreatedAt=Date.now()),`/?${e}=${this.randomValue}`}async scriptTag(e){let r=await this.getScriptUrl(),t=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${r}"${t}></script>`}async protect(e,r){let t=new URL(e.url);if(e.method==="GET"){if(t.pathname==="/"){for(let[l]of t.searchParams)if(await I(this.creds.secret,l)){let f=await this.fetchBundle();return{response:new Response(f??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":f?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&t.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"&&C.test(t.pathname)){let l=e.clone(),f=new Uint8Array(await l.arrayBuffer());if(f.length>0&&f[0]===91)return{response:await this.forwardToApi(e,t.pathname,f)}}if(this.config.excludePaths?.some(l=>t.pathname.startsWith(l)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(l=>t.pathname.startsWith(l)))return{response:null};let o=await k(this.creds.secret,e);if(!o)return{response:new Response(null,{status:401})};let n=o.indexOf(".");if(n<1)return{response:new Response(null,{status:401})};let a=o.slice(0,n),i=o.slice(n+1),c=S(a);if(!c)return{response:new Response(null,{status:401})};let d=r&&this.config.privateKey?await T(r,this.config.privateKey):void 0,u=await this.validateToken(c,a,i,d);return u.valid?{response:null,deviceId:u.deviceId}:{response:new Response(null,{status:401})}}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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,r,t){let o={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[a,i]of Object.entries(this.config.getPlatformSignals(e)))o[a.replace(/[\r\n]/g,"")]=String(i).replace(/[\r\n]/g,"");let n=await this.authedFetch(r,{method:"POST",headers:o,body:t.buffer});if(!n.ok){let a=await n.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${r}: ${n.status} ${a}`)}return n}async validateToken(e,r,t,o){try{let n={tokenHex:e,signature:t,payload:r};o&&(n.encryptedUser=o);let a=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok){let c=await a.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${a.status} ${c}`),{valid:!1}}let i=await a.json();return i.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(i)}`),{valid:i.valid===!0,deviceId:i.deviceId}}catch(n){return console.error("[obfious] Validate error:",n),{valid:!1}}}async authedFetch(e,r){let t=`${this.config.apiUrl}${e}`,o=Date.now().toString(),n=(r.method||"GET").toUpperCase(),a=`${o}.${n}.${e}`,i=await y(this.creds.secret,a),c=new Headers(r.headers);return c.set(x,this.creds.keyId),c.set(P,i),c.set(A,o),fetch(t,{...r,headers:c})}};async function w(s,e=0){let r=Math.floor(Date.now()/3e5)+e,t=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode("obfious-bootstrap-v1:"+r));return g(new Uint8Array(o)).slice(0,10)}async function I(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let r of[-1,0,1])if(await w(s,r)===e)return!0;return!1}function g(s){return Array.from(s,e=>e.toString(16).padStart(2,"0")).join("")}async function y(s,e){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(s),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(e));return g(new Uint8Array(t))}async function T(s,e){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(s));return g(new Uint8Array(t))}function S(s){try{let e=s.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let r=Uint8Array.from(atob(e),t=>t.charCodeAt(0));return r.length<9||r[0]!==33?null:g(r.slice(1,9))}catch{return null}}function O(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",r=crypto.getRandomValues(new Uint8Array(s));return Array.from(r,t=>e[t%e.length]).join("")}var m="0123456789abcdef";function h(s,e){return s.split("").map(r=>{let t=m.indexOf(r);return t<0?r:m[(t+e)%16]}).join("")}async function $(s,e){let r=(await y(s,e)).slice(0,8),t=crypto.getRandomValues(new Uint8Array(1))[0]%2===0?13:14;return h(r,t)+O(4)}async function k(s,e){for(let[r,t]of e.headers){if(!r.startsWith("x-")||r.length<14)continue;let o=r.slice(2),n=o.indexOf("-");if(n<1)continue;let a=o.slice(0,n),i=o.slice(n+1);if(!/^[0-9a-f]+$/.test(a)||i.length<8)continue;let c=i.slice(0,i.length-4);if(c.length!==8||!/^[0-9a-f]{8}$/.test(c))continue;let d=(await y(s,a)).slice(0,8);if(h(c,3)===d||h(c,2)===d)return t}return null}import{Readable as b}from"node:stream";function v(s){let e=s.headers["x-forwarded-proto"]||"http",r=s.headers.host||"localhost",t=`${e}://${r}${s.url}`,o=new Headers;for(let[a,i]of Object.entries(s.headers))i&&o.set(a,Array.isArray(i)?i.join(", "):i);let n=s.method!=="GET"&&s.method!=="HEAD";return new Request(t,{method:s.method,headers:o,body:n?b.toWeb(b.from(s)):null,duplex:"half"})}async function R(s,e){let r={};if(e.headers.forEach((t,o)=>{r[o]=t}),s.writeHead(e.status,r),e.body){let t=e.body.getReader();try{for(;;){let{done:o,value:n}=await t.read();if(o)break;s.write(n)}}finally{t.releaseLock()}}s.end()}function M(s){let{creds:e,getUser:r,...t}=s,o=new p({...t,keyId:e.keyId,secret:e.secret,getClientIp:t.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown"),getPlatformSignals:t.getPlatformSignals??(()=>({}))});return async(n,a,i)=>{try{let c=v(n),d=r?.(n),u=await o.protect(c,d);if(u.response){await R(a,u.response);return}u.deviceId&&(n.obfiousDeviceId=u.deviceId),i()}catch(c){i(c)}}}export{p as Obfious,M as obfiousMiddleware};
package/dist/fastify.js CHANGED
@@ -1 +1 @@
1
- var R="x-obfious-key",v="x-obfious-sig",x="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/;var f=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 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 C(this.creds.secret,c)){let u=await this.fetchBundle();return{response:new Response(u??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":u?"private, max-age=300":"no-store"}})}}}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"&&P.test(r.pathname)){let c=e.clone(),u=new Uint8Array(await c.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(e,r.pathname,u)}}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),a=n.slice(i+1),d=I(o);if(!d)return{response:new Response(null,{status:401})};let g=t&&this.config.privateKey?await T(t,this.config.privateKey):void 0,l=await this.validateToken(d,o,a,g);return l.valid?{response:null,deviceId:l.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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,t,r){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[o,a]of Object.entries(this.config.getPlatformSignals(e)))n[o.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let i=await this.authedFetch(t,{method:"POST",headers:n,body:r.buffer});if(!i.ok){let o=await i.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${t}: ${i.status} ${o}`)}return i}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){let d=await o.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${o.status} ${d}`),{valid:!1}}let a=await o.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(i){return console.error("[obfious] Validate error:",i),{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}`,a=await A(this.creds.secret,o),d=new Headers(t.headers);return d.set(R,this.creds.keyId),d.set(v,a),d.set(x,n),fetch(r,{...t,headers:d})}};async function h(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 C(s,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await h(s,t)===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 I(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 k(s){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(s));return Array.from(t,r=>e[r%e.length]).join("")}import{Readable as y}from"node:stream";function m(s){let e=s.headers["x-forwarded-proto"]||"http",t=s.headers.host||"localhost",r=`${e}://${t}${s.url}`,n=new Headers;for(let[o,a]of Object.entries(s.headers))a&&n.set(o,Array.isArray(a)?a.join(", "):a);let i=s.method!=="GET"&&s.method!=="HEAD";return new Request(r,{method:s.method,headers:n,body:i?y.toWeb(y.from(s)):null,duplex:"half"})}async function H(s,e){let{creds:t,getUser:r,...n}=e,i=new f({...n,keyId:t.keyId,secret:t.secret,getClientIp:n.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});s.addHook("onRequest",async(o,a)=>{let d=m(o.raw),g=r?.(o.raw),l=await i.protect(d,g);if(l.response){let c={};l.response.headers.forEach((w,b)=>{c[b]=w});let u=await l.response.text();a.code(l.response.status).headers(c).send(u);return}l.deviceId&&(o.obfiousDeviceId=l.deviceId)})}export{f as Obfious,H as obfiousPlugin};
1
+ var P="x-obfious-key",A="x-obfious-sig",C="x-obfious-ts",I=/\.(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 w(this.creds.secret);return(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=await $(this.creds.secret,e),this.randomValueCreatedAt=Date.now()),`/?${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){let s=new URL(e.url);if(e.method==="GET"){if(s.pathname==="/"){for(let[d]of s.searchParams)if(await T(this.creds.secret,d)){let u=await this.fetchBundle();return{response:new Response(u??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":u?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&s.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"&&I.test(s.pathname)){let d=e.clone(),u=new Uint8Array(await d.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(e,s.pathname,u)}}if(this.config.excludePaths?.some(d=>s.pathname.startsWith(d)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(d=>s.pathname.startsWith(d)))return{response:null};let n=await H(this.creds.secret,e);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),a=n.slice(i+1),c=O(o);if(!c)return{response:new Response(null,{status:401})};let f=t&&this.config.privateKey?await k(t,this.config.privateKey):void 0,l=await this.validateToken(c,o,a,f);return l.valid?{response:null,deviceId:l.deviceId}:{response:new Response(null,{status:401})}}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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,t,s){let n={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[o,a]of Object.entries(this.config.getPlatformSignals(e)))n[o.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let i=await this.authedFetch(t,{method:"POST",headers:n,body:s.buffer});if(!i.ok){let o=await i.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${t}: ${i.status} ${o}`)}return i}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){let c=await o.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${o.status} ${c}`),{valid:!1}}let a=await o.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(i){return console.error("[obfious] Validate error:",i),{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}`,a=await y(this.creds.secret,o),c=new Headers(t.headers);return c.set(P,this.creds.keyId),c.set(A,a),c.set(C,n),fetch(s,{...t,headers:c})}};async function w(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 T(r,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await w(r,t)===e)return!0;return!1}function p(r){return Array.from(r,e=>e.toString(16).padStart(2,"0")).join("")}async function y(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 k(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 O(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("")}var m="0123456789abcdef";function h(r,e){return r.split("").map(t=>{let s=m.indexOf(t);return s<0?t:m[(s+e)%16]}).join("")}async function $(r,e){let t=(await y(r,e)).slice(0,8),s=crypto.getRandomValues(new Uint8Array(1))[0]%2===0?13:14;return h(t,s)+S(4)}async function H(r,e){for(let[t,s]of e.headers){if(!t.startsWith("x-")||t.length<14)continue;let n=t.slice(2),i=n.indexOf("-");if(i<1)continue;let o=n.slice(0,i),a=n.slice(i+1);if(!/^[0-9a-f]+$/.test(o)||a.length<8)continue;let c=a.slice(0,a.length-4);if(c.length!==8||!/^[0-9a-f]{8}$/.test(c))continue;let f=(await y(r,o)).slice(0,8);if(h(c,3)===f||h(c,2)===f)return s}return null}import{Readable as b}from"node:stream";function x(r){let e=r.headers["x-forwarded-proto"]||"http",t=r.headers.host||"localhost",s=`${e}://${t}${r.url}`,n=new Headers;for(let[o,a]of Object.entries(r.headers))a&&n.set(o,Array.isArray(a)?a.join(", "):a);let i=r.method!=="GET"&&r.method!=="HEAD";return new Request(s,{method:r.method,headers:n,body:i?b.toWeb(b.from(r)):null,duplex:"half"})}async function F(r,e){let{creds:t,getUser:s,...n}=e,i=new g({...n,keyId:t.keyId,secret:t.secret,getClientIp:n.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});r.addHook("onRequest",async(o,a)=>{let c=x(o.raw),f=s?.(o.raw),l=await i.protect(c,f);if(l.response){let d={};l.response.headers.forEach((R,v)=>{d[v]=R});let u=await l.response.text();a.code(l.response.status).headers(d).send(u);return}l.deviceId&&(o.obfiousDeviceId=l.deviceId)})}export{g as Obfious,F as obfiousPlugin};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/;var f=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 p(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 v(this.creds.secret,c)){let u=await this.fetchBundle();return{response:new Response(u??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":u?"private, max-age=300":"no-store"}})}}}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"&&b.test(r.pathname)){let c=e.clone(),u=new Uint8Array(await c.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(e,r.pathname,u)}}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 s=n.indexOf(".");if(s<1)return{response:new Response(null,{status:401})};let i=n.slice(0,s),a=n.slice(s+1),l=x(i);if(!l)return{response:new Response(null,{status:401})};let g=t&&this.config.privateKey?await T(t,this.config.privateKey):void 0,h=await this.validateToken(l,i,a,g);return h.valid?{response:null,deviceId:h.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=P(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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),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,a]of Object.entries(this.config.getPlatformSignals(e)))n[i.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let s=await this.authedFetch(t,{method:"POST",headers:n,body:r.buffer});if(!s.ok){let i=await s.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${t}: ${s.status} ${i}`)}return s}async validateToken(e,t,r,n){try{let s={tokenHex:e,signature:r,payload:t};n&&(s.encryptedUser=n);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!i.ok){let l=await i.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${i.status} ${l}`),{valid:!1}}let a=await i.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(s){return console.error("[obfious] Validate error:",s),{valid:!1}}}async authedFetch(e,t){let r=`${this.config.apiUrl}${e}`,n=Date.now().toString(),s=(t.method||"GET").toUpperCase(),i=`${n}.${s}.${e}`,a=await A(this.creds.secret,i),l=new Headers(t.headers);return l.set(y,this.creds.keyId),l.set(w,a),l.set(m,n),fetch(r,{...t,headers:l})}};async function p(o,e=0){let t=Math.floor(Date.now()/3e5)+e,r=await crypto.subtle.importKey("raw",new TextEncoder().encode(o),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode("obfious-bootstrap-v1:"+t));return d(new Uint8Array(n)).slice(0,10)}async function v(o,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await p(o,t)===e)return!0;return!1}function d(o){return Array.from(o,e=>e.toString(16).padStart(2,"0")).join("")}async function A(o,e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(o),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return d(new Uint8Array(r))}async function T(o,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(o));return d(new Uint8Array(r))}function x(o){try{let e=o.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:d(t.slice(1,9))}catch{return null}}function P(o){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(o));return Array.from(t,r=>e[r%e.length]).join("")}export{f as Obfious};
1
+ var b="x-obfious-key",v="x-obfious-sig",x="x-obfious-ts",A=/\.(json|js|gif|png|woff2|css)$/;var h=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(t){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"},this.creds={keyId:t.keyId,secret:t.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let t=await m(this.creds.secret);return(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=await I(this.creds.secret,t),this.randomValueCreatedAt=Date.now()),`/?${t}=${this.randomValue}`}async scriptTag(t){let e=await this.getScriptUrl(),r=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${r}></script>`}async protect(t,e){let r=new URL(t.url);if(t.method==="GET"){if(r.pathname==="/"){for(let[l]of r.searchParams)if(await P(this.creds.secret,l)){let u=await this.fetchBundle();return{response:new Response(u??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":u?"private, max-age=300":"no-store"}})}}}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(t.method==="POST"&&A.test(r.pathname)){let l=t.clone(),u=new Uint8Array(await l.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(t,r.pathname,u)}}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=await $(this.creds.secret,t);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),c=T(o);if(!c)return{response:new Response(null,{status:401})};let d=e&&this.config.privateKey?await R(e,this.config.privateKey):void 0,y=await this.validateToken(c,o,a,d);return y.valid?{response:null,deviceId:y.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}lastFetchError="";async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():(this.lastFetchError=`API returned ${t.status}`,console.error(`[obfious] Bundle fetch failed: ${t.status} ${t.statusText}`),null)}catch(t){return this.lastFetchError=`${t}`,console.error("[obfious] Bundle fetch error:",t),null}}async forwardToApi(t,e,r){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(t)};if(this.config.getPlatformSignals)for(let[o,a]of Object.entries(this.config.getPlatformSignals(t)))s[o.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let i=await this.authedFetch(e,{method:"POST",headers:s,body:r.buffer});if(!i.ok){let o=await i.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${e}: ${i.status} ${o}`)}return i}async validateToken(t,e,r,s){try{let i={tokenHex:t,signature:r,payload:e};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok){let c=await o.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${o.status} ${c}`),{valid:!1}}let a=await o.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(i){return console.error("[obfious] Validate error:",i),{valid:!1}}}async authedFetch(t,e){let r=`${this.config.apiUrl}${t}`,s=Date.now().toString(),i=(e.method||"GET").toUpperCase(),o=`${s}.${i}.${t}`,a=await p(this.creds.secret,o),c=new Headers(e.headers);return c.set(b,this.creds.keyId),c.set(v,a),c.set(x,s),fetch(r,{...e,headers:c})}};async function m(n,t=0){let e=Math.floor(Date.now()/3e5)+t,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:"+e));return f(new Uint8Array(s)).slice(0,10)}async function P(n,t){if(t.length!==10||!/^[0-9a-f]{10}$/.test(t))return!1;for(let e of[-1,0,1])if(await m(n,e)===t)return!0;return!1}function f(n){return Array.from(n,t=>t.toString(16).padStart(2,"0")).join("")}async function p(n,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(t));return f(new Uint8Array(r))}async function R(n,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(n));return f(new Uint8Array(r))}function T(n){try{let t=n.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),r=>r.charCodeAt(0));return e.length<9||e[0]!==33?null:f(e.slice(1,9))}catch{return null}}function C(n){let t="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",e=crypto.getRandomValues(new Uint8Array(n));return Array.from(e,r=>t[r%t.length]).join("")}var w="0123456789abcdef";function g(n,t){return n.split("").map(e=>{let r=w.indexOf(e);return r<0?e:w[(r+t)%16]}).join("")}async function I(n,t){let e=(await p(n,t)).slice(0,8),r=crypto.getRandomValues(new Uint8Array(1))[0]%2===0?13:14;return g(e,r)+C(4)}async function $(n,t){for(let[e,r]of t.headers){if(!e.startsWith("x-")||e.length<14)continue;let s=e.slice(2),i=s.indexOf("-");if(i<1)continue;let o=s.slice(0,i),a=s.slice(i+1);if(!/^[0-9a-f]+$/.test(o)||a.length<8)continue;let c=a.slice(0,a.length-4);if(c.length!==8||!/^[0-9a-f]{8}$/.test(c))continue;let d=(await p(n,o)).slice(0,8);if(g(c,3)===d||g(c,2)===d)return r}return null}export{h as Obfious};
package/dist/lambda.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",m="x-obfious-sig",w="x-obfious-ts",b=/\.(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 g(this.creds.secret);return this.ensureRandomValue(),`/?${e}=${this.randomValue}`}async scriptTag(e){let r=await this.getScriptUrl(),s=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${r}"${s}></script>`}async protect(e,r){let s=new URL(e.url);if(e.method==="GET"){if(s.pathname==="/"){for(let[d]of s.searchParams)if(await P(this.creds.secret,d)){let l=await this.fetchBundle();return{response:new Response(l??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":l?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&s.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"&&b.test(s.pathname)){let d=e.clone(),l=new Uint8Array(await d.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(e,s.pathname,l)}}if(this.config.excludePaths?.some(d=>s.pathname.startsWith(d)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(d=>s.pathname.startsWith(d)))return{response:null};let o=e.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let i=o.indexOf(".");if(i<1)return{response:new Response(null,{status:401})};let n=o.slice(0,i),c=o.slice(i+1),a=A(n);if(!a)return{response:new Response(null,{status:401})};let f=r&&this.config.privateKey?await R(r,this.config.privateKey):void 0,u=await this.validateToken(a,n,c,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=C(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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,r,s){let o={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[n,c]of Object.entries(this.config.getPlatformSignals(e)))o[n.replace(/[\r\n]/g,"")]=String(c).replace(/[\r\n]/g,"");let i=await this.authedFetch(r,{method:"POST",headers:o,body:s.buffer});if(!i.ok){let n=await i.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${r}: ${i.status} ${n}`)}return i}async validateToken(e,r,s,o){try{let i={tokenHex:e,signature:s,payload:r};o&&(i.encryptedUser=o);let n=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!n.ok){let a=await n.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${n.status} ${a}`),{valid:!1}}let c=await n.json();return c.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(c)}`),{valid:c.valid===!0,deviceId:c.deviceId}}catch(i){return console.error("[obfious] Validate error:",i),{valid:!1}}}async authedFetch(e,r){let s=`${this.config.apiUrl}${e}`,o=Date.now().toString(),i=(r.method||"GET").toUpperCase(),n=`${o}.${i}.${e}`,c=await x(this.creds.secret,n),a=new Headers(r.headers);return a.set(y,this.creds.keyId),a.set(m,c),a.set(w,o),fetch(s,{...r,headers:a})}};async function g(t,e=0){let r=Math.floor(Date.now()/3e5)+e,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-bootstrap-v1:"+r));return p(new Uint8Array(o)).slice(0,10)}async function P(t,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let r of[-1,0,1])if(await g(t,r)===e)return!0;return!1}function p(t){return Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}async function x(t,e){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(e));return p(new Uint8Array(s))}async function R(t,e){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(t));return p(new Uint8Array(s))}function A(t){try{let e=t.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let r=Uint8Array.from(atob(e),s=>s.charCodeAt(0));return r.length<9||r[0]!==33?null:p(r.slice(1,9))}catch{return null}}function C(t){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",r=crypto.getRandomValues(new Uint8Array(t));return Array.from(r,s=>e[s%e.length]).join("")}function I(t){let e=t.headers["x-forwarded-proto"]||t.headers["X-Forwarded-Proto"]||"https",r=t.headers.host||t.headers.Host||"localhost",s=`${e}://${r}${t.path}`;if(t.queryStringParameters){let c=new URLSearchParams;for(let[f,u]of Object.entries(t.queryStringParameters))u!=null&&c.set(f,u);let a=c.toString();a&&(s+=`?${a}`)}let o=new Headers;for(let[c,a]of Object.entries(t.headers))a&&o.set(c,a);let n=t.httpMethod!=="GET"&&t.httpMethod!=="HEAD"&&t.body!=null?t.isBase64Encoded?atob(t.body):t.body:null;return new Request(s,{method:t.httpMethod,headers:o,body:n})}async function T(t){let e={};return t.headers.forEach((r,s)=>{e[s]=r}),{statusCode:t.status,headers:e,body:await t.text()}}function S(t,e){let{creds:r,getUser:s,...o}=t,i=new h({...o,keyId:r.keyId,secret:r.secret,getClientIp:o.getClientIp??(n=>n.headers.get("x-lambda-source-ip")||n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:o.getPlatformSignals??(()=>({}))});return async(n,c)=>{let a=I(n),f=n.requestContext?.identity?.sourceIp||n.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";a.headers.set("x-lambda-source-ip",f);let u=s?.(n),d=await i.protect(a,u);return d.response?T(d.response):(d.deviceId&&(n.headers["x-obfious-device-id"]=String(d.deviceId)),e(n,c))}}export{h as Obfious,S as obfiousHandler};
1
+ var b="x-obfious-key",x="x-obfious-sig",P="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/;var h=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(t){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"},this.creds={keyId:t.keyId,secret:t.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let t=await w(this.creds.secret);return(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=await v(this.creds.secret,t),this.randomValueCreatedAt=Date.now()),`/?${t}=${this.randomValue}`}async scriptTag(t){let e=await this.getScriptUrl(),s=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${s}></script>`}async protect(t,e){let s=new URL(t.url);if(t.method==="GET"){if(s.pathname==="/"){for(let[d]of s.searchParams)if(await A(this.creds.secret,d)){let f=await this.fetchBundle();return{response:new Response(f??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":f?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&s.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(t.method==="POST"&&R.test(s.pathname)){let d=t.clone(),f=new Uint8Array(await d.arrayBuffer());if(f.length>0&&f[0]===91)return{response:await this.forwardToApi(t,s.pathname,f)}}if(this.config.excludePaths?.some(d=>s.pathname.startsWith(d)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(d=>s.pathname.startsWith(d)))return{response:null};let o=await E(this.creds.secret,t);if(!o)return{response:new Response(null,{status:401})};let a=o.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let n=o.slice(0,a),c=o.slice(a+1),i=I(n);if(!i)return{response:new Response(null,{status:401})};let u=e&&this.config.privateKey?await C(e,this.config.privateKey):void 0,l=await this.validateToken(i,n,c,u);return l.valid?{response:null,deviceId:l.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}lastFetchError="";async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():(this.lastFetchError=`API returned ${t.status}`,console.error(`[obfious] Bundle fetch failed: ${t.status} ${t.statusText}`),null)}catch(t){return this.lastFetchError=`${t}`,console.error("[obfious] Bundle fetch error:",t),null}}async forwardToApi(t,e,s){let o={"Content-Type":"application/json","x-obfious-ip":this.getIp(t)};if(this.config.getPlatformSignals)for(let[n,c]of Object.entries(this.config.getPlatformSignals(t)))o[n.replace(/[\r\n]/g,"")]=String(c).replace(/[\r\n]/g,"");let a=await this.authedFetch(e,{method:"POST",headers:o,body:s.buffer});if(!a.ok){let n=await a.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${e}: ${a.status} ${n}`)}return a}async validateToken(t,e,s,o){try{let a={tokenHex:t,signature:s,payload:e};o&&(a.encryptedUser=o);let n=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!n.ok){let i=await n.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${n.status} ${i}`),{valid:!1}}let c=await n.json();return c.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(c)}`),{valid:c.valid===!0,deviceId:c.deviceId}}catch(a){return console.error("[obfious] Validate error:",a),{valid:!1}}}async authedFetch(t,e){let s=`${this.config.apiUrl}${t}`,o=Date.now().toString(),a=(e.method||"GET").toUpperCase(),n=`${o}.${a}.${t}`,c=await y(this.creds.secret,n),i=new Headers(e.headers);return i.set(b,this.creds.keyId),i.set(x,c),i.set(P,o),fetch(s,{...e,headers:i})}};async function w(r,t=0){let e=Math.floor(Date.now()/3e5)+t,s=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode("obfious-bootstrap-v1:"+e));return g(new Uint8Array(o)).slice(0,10)}async function A(r,t){if(t.length!==10||!/^[0-9a-f]{10}$/.test(t))return!1;for(let e of[-1,0,1])if(await w(r,e)===t)return!0;return!1}function g(r){return Array.from(r,t=>t.toString(16).padStart(2,"0")).join("")}async function y(r,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(t));return g(new Uint8Array(s))}async function C(r,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(r));return g(new Uint8Array(s))}function I(r){try{let t=r.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return e.length<9||e[0]!==33?null:g(e.slice(1,9))}catch{return null}}function T(r){let t="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",e=crypto.getRandomValues(new Uint8Array(r));return Array.from(e,s=>t[s%t.length]).join("")}var m="0123456789abcdef";function p(r,t){return r.split("").map(e=>{let s=m.indexOf(e);return s<0?e:m[(s+t)%16]}).join("")}async function v(r,t){let e=(await y(r,t)).slice(0,8),s=crypto.getRandomValues(new Uint8Array(1))[0]%2===0?13:14;return p(e,s)+T(4)}async function E(r,t){for(let[e,s]of t.headers){if(!e.startsWith("x-")||e.length<14)continue;let o=e.slice(2),a=o.indexOf("-");if(a<1)continue;let n=o.slice(0,a),c=o.slice(a+1);if(!/^[0-9a-f]+$/.test(n)||c.length<8)continue;let i=c.slice(0,c.length-4);if(i.length!==8||!/^[0-9a-f]{8}$/.test(i))continue;let u=(await y(r,n)).slice(0,8);if(p(i,3)===u||p(i,2)===u)return s}return null}function S(r){let t=r.headers["x-forwarded-proto"]||r.headers["X-Forwarded-Proto"]||"https",e=r.headers.host||r.headers.Host||"localhost",s=`${t}://${e}${r.path}`;if(r.queryStringParameters){let c=new URLSearchParams;for(let[u,l]of Object.entries(r.queryStringParameters))l!=null&&c.set(u,l);let i=c.toString();i&&(s+=`?${i}`)}let o=new Headers;for(let[c,i]of Object.entries(r.headers))i&&o.set(c,i);let n=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:o,body:n})}async function H(r){let t={};return r.headers.forEach((e,s)=>{t[s]=e}),{statusCode:r.status,headers:t,body:await r.text()}}function k(r,t){let{creds:e,getUser:s,...o}=r,a=new h({...o,keyId:e.keyId,secret:e.secret,getClientIp:o.getClientIp??(n=>n.headers.get("x-lambda-source-ip")||n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:o.getPlatformSignals??(()=>({}))});return async(n,c)=>{let i=S(n),u=n.requestContext?.identity?.sourceIp||n.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";i.headers.set("x-lambda-source-ip",u);let l=s?.(n),d=await a.protect(i,l);return d.response?H(d.response):(d.deviceId&&(n.headers["x-obfious-device-id"]=String(d.deviceId)),t(n,c))}}export{h as Obfious,k as obfiousHandler};
package/dist/nextjs.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/;var d=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 g(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 x(this.creds.secret,c)){let l=await this.fetchBundle();return{response:new Response(l??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":l?"private, max-age=300":"no-store"}})}}}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"&&b.test(r.pathname)){let c=e.clone(),l=new Uint8Array(await c.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(e,r.pathname,l)}}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 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),u=R(i);if(!u)return{response:new Response(null,{status:401})};let h=t&&this.config.privateKey?await v(t,this.config.privateKey):void 0,p=await this.validateToken(u,i,a,h);return p.valid?{response:null,deviceId:p.deviceId}:{response:new Response(null,{status:401})}}ensureRandomValue(){(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=P(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"}lastFetchError="";async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():(this.lastFetchError=`API returned ${e.status}`,console.error(`[obfious] Bundle fetch failed: ${e.status} ${e.statusText}`),null)}catch(e){return this.lastFetchError=`${e}`,console.error("[obfious] Bundle fetch error:",e),null}}async forwardToApi(e,t,r){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(e)};if(this.config.getPlatformSignals)for(let[i,a]of Object.entries(this.config.getPlatformSignals(e)))s[i.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let o=await this.authedFetch(t,{method:"POST",headers:s,body:r.buffer});if(!o.ok){let i=await o.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${t}: ${o.status} ${i}`)}return o}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){let u=await i.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${i.status} ${u}`),{valid:!1}}let a=await i.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(o){return console.error("[obfious] Validate error:",o),{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 C(this.creds.secret,i),u=new Headers(t.headers);return u.set(y,this.creds.keyId),u.set(w,a),u.set(m,s),fetch(r,{...t,headers:u})}};async function g(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 x(n,e){if(e.length!==10||!/^[0-9a-f]{10}$/.test(e))return!1;for(let t of[-1,0,1])if(await g(n,t)===e)return!0;return!1}function f(n){return Array.from(n,e=>e.toString(16).padStart(2,"0")).join("")}async function C(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 v(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 R(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 P(n){let e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",t=crypto.getRandomValues(new Uint8Array(n));return Array.from(t,r=>e[r%e.length]).join("")}function I(n){let{creds:e,...t}=n,r=new d({...t,...e?{keyId:e.keyId,secret:e.secret}:{},getClientIp:t.getClientIp??(s=>s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||"unknown")});return async s=>(await r.protect(s)).response}async function O(n,e){return n.scriptTag({nonce:e})}export{d as Obfious,I as createObfiousMiddleware,O as obfiousScriptTag};
1
+ var b="x-obfious-key",x="x-obfious-sig",C="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/;var d=class{config;creds;randomValue=null;randomValueCreatedAt=0;constructor(t){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"},this.creds={keyId:t.keyId,secret:t.secret}}async getScriptUrl(){if(this.config.scriptPath)return this.config.scriptPath;let t=await m(this.creds.secret);return(!this.randomValue||Date.now()-this.randomValueCreatedAt>9e5)&&(this.randomValue=await I(this.creds.secret,t),this.randomValueCreatedAt=Date.now()),`/?${t}=${this.randomValue}`}async scriptTag(t){let e=await this.getScriptUrl(),r=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${r}></script>`}async protect(t,e){let r=new URL(t.url);if(t.method==="GET"){if(r.pathname==="/"){for(let[u]of r.searchParams)if(await v(this.creds.secret,u)){let l=await this.fetchBundle();return{response:new Response(l??`console.error("[obfious] Failed to load bundle: ${this.lastFetchError}");`,{headers:{"Content-Type":"application/javascript","Cache-Control":l?"private, max-age=300":"no-store"}})}}}if(this.config.scriptPath&&r.pathname===this.config.scriptPath){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&P.test(r.pathname)){let u=t.clone(),l=new Uint8Array(await u.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(t,r.pathname,l)}}if(this.config.excludePaths?.some(u=>r.pathname.startsWith(u)))return{response:null};if(this.config.includePaths&&!this.config.includePaths.some(u=>r.pathname.startsWith(u)))return{response:null};let s=await O(this.creds.secret,t);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),c=A(o);if(!c)return{response:new Response(null,{status:401})};let f=e&&this.config.privateKey?await R(e,this.config.privateKey):void 0,y=await this.validateToken(c,o,a,f);return y.valid?{response:null,deviceId:y.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}lastFetchError="";async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():(this.lastFetchError=`API returned ${t.status}`,console.error(`[obfious] Bundle fetch failed: ${t.status} ${t.statusText}`),null)}catch(t){return this.lastFetchError=`${t}`,console.error("[obfious] Bundle fetch error:",t),null}}async forwardToApi(t,e,r){let s={"Content-Type":"application/json","x-obfious-ip":this.getIp(t)};if(this.config.getPlatformSignals)for(let[o,a]of Object.entries(this.config.getPlatformSignals(t)))s[o.replace(/[\r\n]/g,"")]=String(a).replace(/[\r\n]/g,"");let i=await this.authedFetch(e,{method:"POST",headers:s,body:r.buffer});if(!i.ok){let o=await i.clone().text().catch(()=>"");console.error(`[obfious] forwardToApi ${e}: ${i.status} ${o}`)}return i}async validateToken(t,e,r,s){try{let i={tokenHex:t,signature:r,payload:e};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok){let c=await o.text().catch(()=>"");return console.error(`[obfious] Validate failed: ${o.status} ${c}`),{valid:!1}}let a=await o.json();return a.valid!==!0&&console.error(`[obfious] Validate rejected: ${JSON.stringify(a)}`),{valid:a.valid===!0,deviceId:a.deviceId}}catch(i){return console.error("[obfious] Validate error:",i),{valid:!1}}}async authedFetch(t,e){let r=`${this.config.apiUrl}${t}`,s=Date.now().toString(),i=(e.method||"GET").toUpperCase(),o=`${s}.${i}.${t}`,a=await h(this.creds.secret,o),c=new Headers(e.headers);return c.set(b,this.creds.keyId),c.set(x,a),c.set(C,s),fetch(r,{...e,headers:c})}};async function m(n,t=0){let e=Math.floor(Date.now()/3e5)+t,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:"+e));return g(new Uint8Array(s)).slice(0,10)}async function v(n,t){if(t.length!==10||!/^[0-9a-f]{10}$/.test(t))return!1;for(let e of[-1,0,1])if(await m(n,e)===t)return!0;return!1}function g(n){return Array.from(n,t=>t.toString(16).padStart(2,"0")).join("")}async function h(n,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(t));return g(new Uint8Array(r))}async function R(n,t){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(n));return g(new Uint8Array(r))}function A(n){try{let t=n.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),r=>r.charCodeAt(0));return e.length<9||e[0]!==33?null:g(e.slice(1,9))}catch{return null}}function T(n){let t="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",e=crypto.getRandomValues(new Uint8Array(n));return Array.from(e,r=>t[r%t.length]).join("")}var w="0123456789abcdef";function p(n,t){return n.split("").map(e=>{let r=w.indexOf(e);return r<0?e:w[(r+t)%16]}).join("")}async function I(n,t){let e=(await h(n,t)).slice(0,8),r=crypto.getRandomValues(new Uint8Array(1))[0]%2===0?13:14;return p(e,r)+T(4)}async function O(n,t){for(let[e,r]of t.headers){if(!e.startsWith("x-")||e.length<14)continue;let s=e.slice(2),i=s.indexOf("-");if(i<1)continue;let o=s.slice(0,i),a=s.slice(i+1);if(!/^[0-9a-f]+$/.test(o)||a.length<8)continue;let c=a.slice(0,a.length-4);if(c.length!==8||!/^[0-9a-f]{8}$/.test(c))continue;let f=(await h(n,o)).slice(0,8);if(p(c,3)===f||p(c,2)===f)return r}return null}function S(n){let{creds:t,...e}=n,r=new d({...e,...t?{keyId:t.keyId,secret:t.secret}:{},getClientIp:e.getClientIp??(s=>s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||"unknown")});return async s=>(await r.protect(s)).response}async function H(n,t){return n.scriptTag({nonce:t})}export{d as Obfious,S as createObfiousMiddleware,H as obfiousScriptTag};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obfious/js",
3
- "version": "0.1.14",
3
+ "version": "0.1.17",
4
4
  "description": "Obfious anti-bot protection for JavaScript — CF Workers, Next.js, Express, Fastify, Lambda",
5
5
  "type": "module",
6
6
  "exports": {