@stacksjs/security 0.75.256 → 0.75.257
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/index.js +2 -2
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var ne=import.meta.require;import{Buffer as y}from"buffer";import{config as M}from"@stacksjs/config";import{decrypt as K,encrypt as Ve}from"ts-security-crypto";function O(r){return r.startsWith("base64:")?r.slice(7):r}var L=1,H=2,se="stacks:encrypt:v2",I=600000,P=32,m=16,f=12,ie=64,p=new Map;function oe(r,e,t){return`${t}:${y.from(e).toString("base64")}:${r}`}async function F(r,e,t){let n=oe(r,e,t),s=p.get(n);if(s)return s.hits++,p.delete(n),p.set(n,s),s.key;let i=await crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"PBKDF2"},!1,["deriveKey"]),o=await crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(e),iterations:t,hash:"SHA-256"},i,{name:"AES-GCM",length:P*8},!1,["encrypt","decrypt"]);if(p.set(n,{key:o,hits:1}),p.size>ie){let a=p.keys().next().value;if(a!==void 0)p.delete(a)}return o}function _(r){if(!r.startsWith("base64:"))return null;let e=y.from(r.slice(7),"base64");return e.length>=P?new Uint8Array(e):null}async function V(r,e){let t=await crypto.subtle.importKey("raw",new Uint8Array(r),"HKDF",!1,["deriveKey"]);return await crypto.subtle.deriveKey({name:"HKDF",hash:"SHA-256",salt:new Uint8Array(e),info:new TextEncoder().encode(se)},t,{name:"AES-GCM",length:P*8},!1,["encrypt","decrypt"])}async function ae(r,e){let t=crypto.getRandomValues(new Uint8Array(m)),n=crypto.getRandomValues(new Uint8Array(f)),s=await V(e,t),i=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:n},s,new TextEncoder().encode(r))),o=new Uint8Array(1+m+f+i.byteLength);return o[0]=H,o.set(t,1),o.set(n,1+m),o.set(i,1+m+f),y.from(o).toString("base64")}async function ce(r,e){let t=y.from(r,"base64");if(t.length<1+m+f+16)return null;if(t[0]!==H)return null;let n=new Uint8Array(t.subarray(1,1+m)),s=new Uint8Array(t.subarray(1+m,1+m+f)),i=new Uint8Array(t.subarray(1+m+f)),o=await V(e,n);try{return new TextDecoder().decode(await crypto.subtle.decrypt({name:"AES-GCM",iv:s},o,i))}catch{return null}}async function ue(r,e){let t=crypto.getRandomValues(new Uint8Array(m)),n=crypto.getRandomValues(new Uint8Array(f)),s=await F(e,t,I),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:n},s,new TextEncoder().encode(r)),o=new Uint8Array(i),a=new Uint8Array(1+m+f+o.byteLength);return a[0]=L,a.set(t,1),a.set(n,1+m),a.set(o,1+m+f),y.from(a).toString("base64")}async function me(r,e){let t=y.from(r,"base64");if(t.length<1+m+f+16)return null;if(t[0]!==L)return null;let n=new Uint8Array(t.subarray(1,1+m)),s=new Uint8Array(t.subarray(1+m,1+m+f)),i=new Uint8Array(t.subarray(1+m+f)),o=await F(e,n,I);try{let a=await crypto.subtle.decrypt({name:"AES-GCM",iv:s},o,i);return new TextDecoder().decode(a)}catch{return null}}async function Ge(r,e){if(!r&&r!=="")throw Error("encrypt() requires a string message");let t=e||M.app.key;if(!t)throw Error("APP_KEY is not defined");let n=_(t);if(n)return await ae(r,n);return await ue(r,O(t))}async function De(r,e){if(!r)throw Error("decrypt() requires a non-empty encrypted string");let t=e||M.app.key;if(!t)throw Error("APP_KEY is not defined");let n=O(t),s=_(t);if(s){let o=await ce(r,s);if(o!==null)return o}let i=await me(r,n);if(i!==null)return i;try{return await K(r,n)}catch(o){if(n!==t)try{return await K(r,t)}catch{}throw o}}import{timingSafeEqual as G}from"crypto";import{base64Encode as Ye,hashPassword as q,verifyPassword as D}from"ts-security-crypto";var w=null;function S(){if(w)return w;try{let{hashing:r}=ne("@stacksjs/config");return w=r||{},w}catch{return console.warn("[Security] Failed to load hashing config, using defaults"),w={driver:"bcrypt",bcrypt:{rounds:12},argon2:{memory:65536,time:2}},w}}function W(r){if(!r||typeof r!=="string")return"unknown";if(r.startsWith("$2a$")||r.startsWith("$2b$")||r.startsWith("$2y$"))return"bcrypt";if(r.startsWith("$argon2id$"))return"argon2id";if(r.startsWith("$argon2i$"))return"argon2i";if(r.startsWith("$argon2d$"))return"argon2d";return"unknown"}function le(r){let e=W(r),t={};if(e==="bcrypt"){let n=r.match(/^\$2[aby]\$(\d{2})\$/);if(n&&n[1]!==void 0)t.rounds=Number.parseInt(n[1],10)}else if(e==="argon2id"||e==="argon2i"||e==="argon2d"){let n=r.match(/v=(\d+)/),s=r.match(/m=(\d+)/),i=r.match(/t=(\d+)/),o=r.match(/p=(\d+)/);if(n&&n[1]!==void 0)t.version=Number.parseInt(n[1],10);if(s&&s[1]!==void 0)t.memory=Number.parseInt(s[1],10);if(i&&i[1]!==void 0)t.rounds=Number.parseInt(i[1],10);if(o&&o[1]!==void 0)t.parallelism=Number.parseInt(o[1],10)}return{algorithm:e,options:t}}function Je(r,e){let t=le(r),n=S(),s=e?.algorithm||n.driver||"bcrypt",i=(c)=>{if(c==="argon2")return"argon2id";return c},o=i(t.algorithm),a=i(s);if(o!==a)return!0;if(o==="bcrypt"){let c=e?.rounds||n.bcrypt?.rounds||12;if(t.options.rounds!==c)return!0}if(o.startsWith("argon2")){let c=e?.memory||n.argon2?.memory||65536,u=e?.time||n.argon2?.time||2;if(t.options.memory!==c||t.options.rounds!==u)return!0}return!1}async function Xe(r,e){let t=S(),n=e?.algorithm||t.driver||"bcrypt";if(n==="bcrypt")return await fe(r,e?.rounds);if(n==="argon2"||n==="argon2id"||n==="argon2i"||n==="argon2d")return await ge(r,{type:n==="argon2"?"argon2id":n,memory:e?.memory,time:e?.time});throw Error(`Unsupported hashing algorithm: ${n}`)}async function je(r,e){if(!r||!e)return!1;if(W(e)==="unknown")try{return await D(r,e)}catch{return!1}return await D(r,e)}async function fe(r,e){let t=S(),n=e||t.bcrypt?.rounds||12;return await q(r,{algorithm:"bcrypt",cost:n})}async function ge(r,e){let t=S(),n=e?.type||"argon2id",s=e?.memory||t.argon2?.memory||65536,i=e?.time||t.argon2?.time||2;return await q(r,{algorithm:n,memoryCost:s,timeCost:i})}function Qe(r,e){let t=Buffer.from(r),n=Buffer.from(e);if(t.length!==n.length)return G(t,t),!1;return G(t,n)}import{generateKey as he}from"ts-security-crypto";function tt(){return he(32)}import{createHmac as de,randomBytes as pe,timingSafeEqual as z}from"crypto";class l extends Error{reason;constructor(r,e="mismatch"){super(r);this.name="InvalidWebhookSignature",this.reason=e}}function T(r){if(!/^[0-9a-f]*$/i.test(r)||r.length%2!==0)throw new l("Signature is not valid hex","malformed");return Buffer.from(r,"hex")}function k(r,e){if(r.length!==e.length)return z(r,r),!1;return z(r,e)}function U(r,e,t){return de(t,r).update(e).digest()}function ye(r,e){let t=r.split(","),n=Number.NaN,s=[];for(let i of t){let o=i.indexOf("=");if(o===-1)continue;let a=i.slice(0,o).trim(),c=i.slice(o+1).trim();if(a==="t")n=Number.parseInt(c,10);else if(a==="v1")s.push(c)}if(!Number.isFinite(n)||s.length===0)throw new l(`${e} header is malformed`,"malformed");return{timestamp:n,signatures:s}}function Y(r,e,t,n,s){if(!r)throw new l(`${s.name} secret is empty`,"missing");if(!e)throw new l(`${s.header} header is missing`,"missing");let{timestamp:i,signatures:o}=ye(e,s.header),a=n.toleranceSeconds??300,c=Math.floor((n.now??Date.now())/1000);if(Math.abs(c-i)>a)throw new l(`${s.name} timestamp is outside tolerance window`,"expired");let u=typeof t==="string"?t:t.toString("utf8"),g=U(r,`${i}.${u}`,"sha256");for(let b of o){let x;try{x=T(b)}catch{continue}if(k(g,x))return!0}throw new l(`${s.name} signature did not match`,"mismatch")}function we(r,e,t,n={}){return Y(r,e,t,n,{name:"Stripe webhook",header:"Stripe-Signature"})}function be(r,e,t,n={}){return Y(r,e,t,n,{name:"Webhook",header:"Signature"})}function st(r,e,t={}){let n=(Array.isArray(r)?r:[r]).filter((a)=>typeof a==="string"&&a.length>0);if(!n.length)throw new l("Webhook signing secret is empty","missing");let s=t.timestamp??Math.floor(Date.now()/1000);if(!Number.isInteger(s)||s<0)throw new l("Webhook timestamp must be whole unix seconds","malformed");let i=typeof e==="string"?e:e.toString("utf8"),o=n.map((a)=>U(a,`${s}.${i}`,"sha256").toString("hex"));return{timestamp:s,signatures:o,header:[`t=${s}`,...o.map((a)=>`v1=${a}`)].join(",")}}function it(r="whsec_"){return`${r}${pe(32).toString("base64url")}`}function xe(r,e,t){if(!r)throw new l("GitHub webhook secret is empty","missing");if(!e)throw new l("X-Hub-Signature-256 header is missing","missing");let n="sha256=";if(!e.startsWith(n))throw new l("X-Hub-Signature-256 has wrong algorithm prefix","malformed");let s=T(e.slice(n.length)),i=U(r,t,"sha256");if(!k(i,s))throw new l("GitHub webhook signature did not match","mismatch");return!0}function ve(r,e,t,n="sha256"){if(!r)throw new l("HMAC secret is empty","missing");if(!t)throw new l("Signature header is missing","missing");let s=t.indexOf("="),i=s!==-1&&/^[a-z0-9]+$/i.test(t.slice(0,s))?t.slice(s+1):t,o=T(i),a=U(r,e,n);if(!k(a,o))throw new l("HMAC signature did not match","mismatch");return!0}function ot(r,e,t,n,s={}){switch(r){case"stripe":return we(e,t,n,s);case"github":return xe(e,t,n);case"generic":return ve(e,n,t,s.algorithm??"sha256");case"signed":return be(e,t,n,s);default:throw new l(`Unknown webhook provider: ${String(r)}`,"malformed")}}import{createHash as Ae,randomBytes as $e}from"crypto";function Re(r){return Ae("sha256").update(r,"utf8").digest("hex")}function Se(r,e=""){let t=e&&r.startsWith(e)?r.slice(e.length):r;if(t.length<=8)return`${e}\u2026`;return`${e}${t.slice(0,4)}\u2026${t.slice(-4)}`}function ut(r="",e=32){if(!Number.isInteger(e)||e<16)throw Error("An API key needs at least 16 random bytes");let t=`${r}${$e(e).toString("base64url")}`;return{key:t,hash:Re(t),hint:Se(t,r)}}import{lookup as Ue}from"dns/promises";import{isIP as v}from"net";class h extends Error{reason;constructor(r,e){super(r);this.name="OutboundUrlError",this.reason=e}}function X(r){let e=r.split(".");if(e.length!==4)return null;let t=e.map((n)=>/^\d{1,3}$/.test(n)?Number(n):Number.NaN);return t.every((n)=>Number.isInteger(n)&&n>=0&&n<=255)?t:null}function E(r){let e=X(r);if(!e)return!0;let[t,n,s]=e;return t===0||t===10||t===100&&n>=64&&n<=127||t===127||t===169&&n===254||t===172&&n>=16&&n<=31||t===192&&n===0&&(s===0||s===2)||t===192&&n===88&&s===99||t===192&&n===168||t===198&&(n===18||n===19)||t===198&&n===51&&s===100||t===203&&n===0&&s===113||t>=224}function Ee(r){let e=r.toLowerCase(),t=e.indexOf("%");if(t!==-1)e=e.slice(0,t);let n=e.match(/^(.*:)(\d+\.\d+\.\d+\.\d+)$/);if(n){let u=X(n[2]);if(!u)return null;e=`${n[1]}${(u[0]<<8|u[1]).toString(16)}:${(u[2]<<8|u[3]).toString(16)}`}let s=e.split("::");if(s.length>2)return null;let i=s[0]?s[0].split(":"):[],o=s.length===2&&s[1]?s[1].split(":"):[],a=8-i.length-o.length;if(s.length===1&&a!==0||a<0)return null;let c=[...i,...Array.from({length:s.length===2?a:0},()=>"0"),...o];if(c.length!==8||c.some((u)=>!/^[0-9a-f]{1,4}$/.test(u)))return null;return c.map((u)=>Number.parseInt(u,16))}function Pe(r){let e=Ee(r);if(!e)return!0;let t=`${e[6]>>8}.${e[6]&255}.${e[7]>>8}.${e[7]&255}`;if(e.slice(0,7).every((n)=>n===0))return!0;if(e.slice(0,5).every((n)=>n===0)&&e[5]===65535)return E(t);if(e[0]===100&&e[1]===65435&&e.slice(2,6).every((n)=>n===0))return E(t);if(e[0]===8194)return E(`${e[1]>>8}.${e[1]&255}.${e[2]>>8}.${e[2]&255}`);return(e[0]&65024)===64512||(e[0]&65472)===65152||(e[0]&65280)===65280||e[0]===8193&&e[1]===3512||e[0]===8193&&e[1]<512||e[0]===256}function B(r){let e=r.replace(/^\[|\]$/g,""),t=v(e.split("%")[0]);if(t===4)return E(e);if(t===6)return Pe(e);return!0}var Te=[".localhost",".local",".internal",".localdomain",".home.arpa"];function ke(r,e={}){let t;try{t=new URL(r)}catch{return{reason:"invalid",message:"Not a valid URL"}}if(!(e.allowHttp?["https:","http:"]:["https:"]).includes(t.protocol))return{reason:"scheme",message:e.allowHttp?"The URL must start with https:// or http://":"The URL must start with https://"};if(t.username||t.password)return{reason:"credentials",message:"The URL must not contain a username or password"};if(e.allowPrivate)return null;let s=t.hostname.replace(/^\[|\]$/g,"").toLowerCase().replace(/\.$/,"");if(!s)return{reason:"invalid",message:"Not a valid URL"};if(v(s.split("%")[0]))return B(s)?{reason:"private",message:"The URL points at a private or local address"}:null;if(s==="localhost"||!s.includes(".")||Te.some((i)=>s.endsWith(i)))return{reason:"private",message:"The URL points at a private or local address"};return null}async function j(r){return(await Ue(r,{all:!0,verbatim:!0})).map((t)=>t.address)}async function Be(r,e={}){let t=ke(r,e);if(t)throw new h(t.message,t.reason);let n=new URL(r);if(e.allowPrivate)return n;let s=n.hostname.replace(/^\[|\]$/g,"");if(v(s.split("%")[0]))return n;let i;try{i=await(e.resolve??j)(s)}catch{throw new h(`Could not resolve ${s}`,"unresolvable")}if(!i.length)throw new h(`Could not resolve ${s}`,"unresolvable");if(i.some(B))throw new h("The URL points at a private or local address","private");return n}async function Ce(r,e){let t=await Be(r,e),n=t.hostname.replace(/^\[|\]$/g,"");if(v(n.split("%")[0])||e.allowPrivate)return{parsed:t,addresses:[]};let s=await(e.resolve??j)(n);return{parsed:t,addresses:s}}var Ne=new Set([301,302,303,307,308]),Ke=["authorization","cookie","proxy-authorization"];function Me(r,e,t){if(!e)return[r.href,t];let n=new URL(r.href);n.hostname=v(e)===6?`[${e}]`:e;let s=new Headers(t.headers);s.set("Host",r.host);let i={...t,headers:s};if(r.protocol==="https:")i.tls={serverName:r.hostname.replace(/^\[|\]$/g,"")};return[n.href,i]}async function J(r,e){let t=Number(r.headers.get("content-length")??Number.NaN);if(Number.isFinite(t)&&t>e)throw new h(`The response is ${t} bytes, over the ${e} byte limit`,"too-large");if(!r.body)return r;let n=[],s=0,i=r.body.getReader();try{for(;;){let{done:c,value:u}=await i.read();if(c)break;if(s+=u.byteLength,s>e)throw await i.cancel(),new h(`The response is over the ${e} byte limit`,"too-large");n.push(u)}}finally{i.releaseLock()}let o=new Uint8Array(s),a=0;for(let c of n)o.set(c,a),a+=c.byteLength;return new Response(o,{status:r.status,statusText:r.statusText,headers:r.headers})}async function gt(r,e={},t={}){let n=t.maxRedirects??3,s=t.maxBytes??5242880,i=t.timeoutMs??1e4,o=AbortSignal.timeout(i),a=e.signal?AbortSignal.any([e.signal,o]):o,c=e.body==null||typeof e.body==="string"||e.body instanceof Uint8Array||e.body instanceof ArrayBuffer,u=r,g={...e,signal:a,redirect:"manual"},b="";for(let x=0;;x++){let{parsed:A,addresses:Z}=await Ce(u,t);if(b&&A.origin!==b){let R=new Headers(g.headers);for(let re of Ke)R.delete(re);g={...g,headers:R}}b=A.origin;let[ee,te]=Me(A,Z.find((R)=>!B(R)),g),d=await fetch(ee,te);if(!Ne.has(d.status))return await J(d,s);let C=d.headers.get("location");if(!C)return await J(d,s);if(x>=n)throw new h(`The URL redirected more than ${n} times`,"redirects");if(!c)throw new h("The URL redirected, and a streaming request body cannot be sent again","unreplayable-body");let N=(g.method??"GET").toUpperCase();if(d.status===303||(d.status===301||d.status===302)&&N!=="GET"&&N!=="HEAD")g={...g,method:"GET",body:void 0};u=new URL(C,A).href}}import{devNull as Oe}from"os";class Q extends Error{constructor(){super("Regular expression exceeded its execution budget");this.name="RegexTimeoutError"}}var Le=`
|
|
2
|
+
var se=import.meta.require;import{Buffer as y}from"buffer";import{config as M}from"@stacksjs/config";import{decrypt as K,encrypt as Ge}from"ts-security-crypto";function O(t){return t.startsWith("base64:")?t.slice(7):t}var L=1,I=2,oe="stacks:encrypt:v2",H=600000,B=32,m=16,f=12,ie=64,p=new Map;function ae(t,e,r){return`${r}:${y.from(e).toString("base64")}:${t}`}async function F(t,e,r){let n=ae(t,e,r),s=p.get(n);if(s)return s.hits++,p.delete(n),p.set(n,s),s.key;let o=await crypto.subtle.importKey("raw",new TextEncoder().encode(t),{name:"PBKDF2"},!1,["deriveKey"]),i=await crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(e),iterations:r,hash:"SHA-256"},o,{name:"AES-GCM",length:B*8},!1,["encrypt","decrypt"]);if(p.set(n,{key:i,hits:1}),p.size>ie){let a=p.keys().next().value;if(a!==void 0)p.delete(a)}return i}function _(t){if(!t.startsWith("base64:"))return null;let e=y.from(t.slice(7),"base64");return e.length>=B?new Uint8Array(e):null}async function V(t,e){let r=await crypto.subtle.importKey("raw",new Uint8Array(t),"HKDF",!1,["deriveKey"]);return await crypto.subtle.deriveKey({name:"HKDF",hash:"SHA-256",salt:new Uint8Array(e),info:new TextEncoder().encode(oe)},r,{name:"AES-GCM",length:B*8},!1,["encrypt","decrypt"])}async function ce(t,e){let r=crypto.getRandomValues(new Uint8Array(m)),n=crypto.getRandomValues(new Uint8Array(f)),s=await V(e,r),o=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:n},s,new TextEncoder().encode(t))),i=new Uint8Array(1+m+f+o.byteLength);return i[0]=I,i.set(r,1),i.set(n,1+m),i.set(o,1+m+f),y.from(i).toString("base64")}async function ue(t,e){let r=y.from(t,"base64");if(r.length<1+m+f+16)return null;if(r[0]!==I)return null;let n=new Uint8Array(r.subarray(1,1+m)),s=new Uint8Array(r.subarray(1+m,1+m+f)),o=new Uint8Array(r.subarray(1+m+f)),i=await V(e,n);try{return new TextDecoder().decode(await crypto.subtle.decrypt({name:"AES-GCM",iv:s},i,o))}catch{return null}}async function me(t,e){let r=crypto.getRandomValues(new Uint8Array(m)),n=crypto.getRandomValues(new Uint8Array(f)),s=await F(e,r,H),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:n},s,new TextEncoder().encode(t)),i=new Uint8Array(o),a=new Uint8Array(1+m+f+i.byteLength);return a[0]=L,a.set(r,1),a.set(n,1+m),a.set(i,1+m+f),y.from(a).toString("base64")}async function le(t,e){let r=y.from(t,"base64");if(r.length<1+m+f+16)return null;if(r[0]!==L)return null;let n=new Uint8Array(r.subarray(1,1+m)),s=new Uint8Array(r.subarray(1+m,1+m+f)),o=new Uint8Array(r.subarray(1+m+f)),i=await F(e,n,H);try{let a=await crypto.subtle.decrypt({name:"AES-GCM",iv:s},i,o);return new TextDecoder().decode(a)}catch{return null}}async function De(t,e){if(!t&&t!=="")throw Error("encrypt() requires a string message");let r=e||M.app.key;if(!r)throw Error("APP_KEY is not defined");let n=_(r);if(n)return await ce(t,n);return await me(t,O(r))}async function qe(t,e){if(!t)throw Error("decrypt() requires a non-empty encrypted string");let r=e||M.app.key;if(!r)throw Error("APP_KEY is not defined");let n=O(r),s=_(r);if(s){let i=await ue(t,s);if(i!==null)return i}let o=await le(t,n);if(o!==null)return o;try{return await K(t,n)}catch(i){if(n!==r)try{return await K(t,r)}catch{}throw i}}import{timingSafeEqual as G}from"crypto";import{base64Encode as Je,hashPassword as q,verifyPassword as D}from"ts-security-crypto";var w=null;function R(){if(w)return w;try{let{hashing:t}=se("@stacksjs/config");return w=t||{},w}catch{return console.warn("[Security] Failed to load hashing config, using defaults"),w={driver:"bcrypt",bcrypt:{rounds:12},argon2:{memory:65536,time:2}},w}}function W(t){if(!t||typeof t!=="string")return"unknown";if(t.startsWith("$2a$")||t.startsWith("$2b$")||t.startsWith("$2y$"))return"bcrypt";if(t.startsWith("$argon2id$"))return"argon2id";if(t.startsWith("$argon2i$"))return"argon2i";if(t.startsWith("$argon2d$"))return"argon2d";return"unknown"}function fe(t){let e=W(t),r={};if(e==="bcrypt"){let n=t.match(/^\$2[aby]\$(\d{2})\$/);if(n&&n[1]!==void 0)r.rounds=Number.parseInt(n[1],10)}else if(e==="argon2id"||e==="argon2i"||e==="argon2d"){let n=t.match(/v=(\d+)/),s=t.match(/m=(\d+)/),o=t.match(/t=(\d+)/),i=t.match(/p=(\d+)/);if(n&&n[1]!==void 0)r.version=Number.parseInt(n[1],10);if(s&&s[1]!==void 0)r.memory=Number.parseInt(s[1],10);if(o&&o[1]!==void 0)r.rounds=Number.parseInt(o[1],10);if(i&&i[1]!==void 0)r.parallelism=Number.parseInt(i[1],10)}return{algorithm:e,options:r}}function Xe(t,e){let r=fe(t),n=R(),s=e?.algorithm||n.driver||"bcrypt",o=(c)=>{if(c==="argon2")return"argon2id";return c},i=o(r.algorithm),a=o(s);if(i!==a)return!0;if(i==="bcrypt"){let c=e?.rounds||n.bcrypt?.rounds||12;if(r.options.rounds!==c)return!0}if(i.startsWith("argon2")){let c=e?.memory||n.argon2?.memory||65536,u=e?.time||n.argon2?.time||2;if(r.options.memory!==c||r.options.rounds!==u)return!0}return!1}async function je(t,e){let r=R(),n=e?.algorithm||r.driver||"bcrypt";if(n==="bcrypt")return await ge(t,e?.rounds);if(n==="argon2"||n==="argon2id"||n==="argon2i"||n==="argon2d")return await he(t,{type:n==="argon2"?"argon2id":n,memory:e?.memory,time:e?.time});throw Error(`Unsupported hashing algorithm: ${n}`)}async function Qe(t,e){if(!t||!e)return!1;if(W(e)==="unknown")try{return await D(t,e)}catch{return!1}return await D(t,e)}async function ge(t,e){let r=R(),n=e||r.bcrypt?.rounds||12;return await q(t,{algorithm:"bcrypt",cost:n})}async function he(t,e){let r=R(),n=e?.type||"argon2id",s=e?.memory||r.argon2?.memory||65536,o=e?.time||r.argon2?.time||2;return await q(t,{algorithm:n,memoryCost:s,timeCost:o})}function Ze(t,e){let r=Buffer.from(t),n=Buffer.from(e);if(r.length!==n.length)return G(r,r),!1;return G(r,n)}import{generateKey as de}from"ts-security-crypto";function rt(){return de(32)}import{createHmac as pe,randomBytes as ye,timingSafeEqual as z}from"crypto";class l extends Error{reason;constructor(t,e="mismatch"){super(t);this.name="InvalidWebhookSignature",this.reason=e}}function k(t){if(!/^[0-9a-f]*$/i.test(t)||t.length%2!==0)throw new l("Signature is not valid hex","malformed");return Buffer.from(t,"hex")}function C(t,e){if(t.length!==e.length)return z(t,t),!1;return z(t,e)}function S(t,e,r){return pe(r,t).update(e).digest()}function we(t,e){let r=t.split(","),n=Number.NaN,s=[];for(let o of r){let i=o.indexOf("=");if(i===-1)continue;let a=o.slice(0,i).trim(),c=o.slice(i+1).trim();if(a==="t")n=Number.parseInt(c,10);else if(a==="v1")s.push(c)}if(!Number.isFinite(n)||s.length===0)throw new l(`${e} header is malformed`,"malformed");return{timestamp:n,signatures:s}}function Y(t,e,r,n,s){if(!t)throw new l(`${s.name} secret is empty`,"missing");if(!e)throw new l(`${s.header} header is missing`,"missing");let{timestamp:o,signatures:i}=we(e,s.header),a=n.toleranceSeconds??300,c=Math.floor((n.now??Date.now())/1000);if(Math.abs(c-o)>a)throw new l(`${s.name} timestamp is outside tolerance window`,"expired");let u=typeof r==="string"?r:r.toString("utf8"),g=S(t,`${o}.${u}`,"sha256");for(let b of i){let x;try{x=k(b)}catch{continue}if(C(g,x))return!0}throw new l(`${s.name} signature did not match`,"mismatch")}function be(t,e,r,n={}){return Y(t,e,r,n,{name:"Stripe webhook",header:"Stripe-Signature"})}function xe(t,e,r,n={}){return Y(t,e,r,n,{name:"Webhook",header:"Signature"})}function ot(t,e,r={}){let n=(Array.isArray(t)?t:[t]).filter((a)=>typeof a==="string"&&a.length>0);if(!n.length)throw new l("Webhook signing secret is empty","missing");let s=r.timestamp??Math.floor(Date.now()/1000);if(!Number.isInteger(s)||s<0)throw new l("Webhook timestamp must be whole unix seconds","malformed");let o=typeof e==="string"?e:e.toString("utf8"),i=n.map((a)=>S(a,`${s}.${o}`,"sha256").toString("hex"));return{timestamp:s,signatures:i,header:[`t=${s}`,...i.map((a)=>`v1=${a}`)].join(",")}}function it(t="whsec_"){return`${t}${ye(32).toString("base64url")}`}function ve(t,e,r){if(!t)throw new l("GitHub webhook secret is empty","missing");if(!e)throw new l("X-Hub-Signature-256 header is missing","missing");let n="sha256=";if(!e.startsWith(n))throw new l("X-Hub-Signature-256 has wrong algorithm prefix","malformed");let s=k(e.slice(n.length)),o=S(t,r,"sha256");if(!C(o,s))throw new l("GitHub webhook signature did not match","mismatch");return!0}function Ae(t,e,r,n="sha256"){if(!t)throw new l("HMAC secret is empty","missing");if(!r)throw new l("Signature header is missing","missing");let s=r.indexOf("="),o=s!==-1&&/^[a-z0-9]+$/i.test(r.slice(0,s))?r.slice(s+1):r,i=k(o),a=S(t,e,n);if(!C(a,i))throw new l("HMAC signature did not match","mismatch");return!0}function at(t,e,r,n,s={}){switch(t){case"stripe":return be(e,r,n,s);case"github":return ve(e,r,n);case"generic":return Ae(e,n,r,s.algorithm??"sha256");case"signed":return xe(e,r,n,s);default:throw new l(`Unknown webhook provider: ${String(t)}`,"malformed")}}import{createHash as Re,randomBytes as $e}from"crypto";function Se(t){return Re("sha256").update(t,"utf8").digest("hex")}function Ee(t,e=""){let r=e&&t.startsWith(e)?t.slice(e.length):t;if(r.length<=8)return`${e}\u2026`;return`${e}${r.slice(0,4)}\u2026${r.slice(-4)}`}function mt(t="",e=32){if(!Number.isInteger(e)||e<16)throw Error("An API key needs at least 16 random bytes");let r=`${t}${$e(e).toString("base64url")}`;return{key:r,hash:Se(r),hint:Ee(r,t)}}import{lookup as Ue}from"dns/promises";import{isIP as U}from"net";class d extends Error{reason;constructor(t,e){super(t);this.name="OutboundUrlError",this.reason=e}}function X(t){let e=t.split(".");if(e.length!==4)return null;let r=e.map((n)=>/^\d{1,3}$/.test(n)?Number(n):Number.NaN);return r.every((n)=>Number.isInteger(n)&&n>=0&&n<=255)?r:null}function E(t){let e=X(t);if(!e)return!0;let[r,n,s]=e;return r===0||r===10||r===100&&n>=64&&n<=127||r===127||r===169&&n===254||r===172&&n>=16&&n<=31||r===192&&n===0&&(s===0||s===2)||r===192&&n===88&&s===99||r===192&&n===168||r===198&&(n===18||n===19)||r===198&&n===51&&s===100||r===203&&n===0&&s===113||r>=224}function Pe(t){let e=t.toLowerCase(),r=e.indexOf("%");if(r!==-1)e=e.slice(0,r);let n=e.match(/^(.*:)(\d+\.\d+\.\d+\.\d+)$/);if(n){let u=X(n[2]);if(!u)return null;e=`${n[1]}${(u[0]<<8|u[1]).toString(16)}:${(u[2]<<8|u[3]).toString(16)}`}let s=e.split("::");if(s.length>2)return null;let o=s[0]?s[0].split(":"):[],i=s.length===2&&s[1]?s[1].split(":"):[],a=8-o.length-i.length;if(s.length===1&&a!==0||a<0)return null;let c=[...o,...Array.from({length:s.length===2?a:0},()=>"0"),...i];if(c.length!==8||c.some((u)=>!/^[0-9a-f]{1,4}$/.test(u)))return null;return c.map((u)=>Number.parseInt(u,16))}function Te(t){let e=Pe(t);if(!e)return!0;let r=`${e[6]>>8}.${e[6]&255}.${e[7]>>8}.${e[7]&255}`;if(e.slice(0,7).every((n)=>n===0))return!0;if(e.slice(0,5).every((n)=>n===0)&&e[5]===65535)return E(r);if(e[0]===100&&e[1]===65435&&e.slice(2,6).every((n)=>n===0))return E(r);if(e[0]===8194)return E(`${e[1]>>8}.${e[1]&255}.${e[2]>>8}.${e[2]&255}`);return(e[0]&65024)===64512||(e[0]&65472)===65152||(e[0]&65280)===65280||e[0]===8193&&e[1]===3512||e[0]===8193&&e[1]<512||e[0]===256}function j(t){let e=t.replace(/^\[|\]$/g,""),r=U(e.split("%")[0]);if(r===4)return E(e);if(r===6)return Te(e);return!0}var Be=[".localhost",".local",".internal",".localdomain",".home.arpa"];function ke(t,e={}){let r;try{r=new URL(t)}catch{return{reason:"invalid",message:"Not a valid URL"}}if(!(e.allowHttp?["https:","http:"]:["https:"]).includes(r.protocol))return{reason:"scheme",message:e.allowHttp?"The URL must start with https:// or http://":"The URL must start with https://"};if(r.username||r.password)return{reason:"credentials",message:"The URL must not contain a username or password"};if(e.allowPrivate)return null;let s=r.hostname.replace(/^\[|\]$/g,"").toLowerCase().replace(/\.$/,"");if(!s)return{reason:"invalid",message:"Not a valid URL"};if(U(s.split("%")[0]))return j(s)?{reason:"private",message:"The URL points at a private or local address"}:null;if(s==="localhost"||!s.includes(".")||Be.some((o)=>s.endsWith(o)))return{reason:"private",message:"The URL points at a private or local address"};return null}async function Ce(t){return(await Ue(t,{all:!0,verbatim:!0})).map((r)=>r.address)}async function ht(t,e={}){return(await Q(t,e)).parsed}async function Q(t,e){let r=ke(t,e);if(r)throw new d(r.message,r.reason);let n=new URL(t),s=n.hostname.replace(/^\[|\]$/g,"");if(e.allowPrivate||U(s.split("%")[0]))return{parsed:n,addresses:[]};let o;try{o=await(e.resolve??Ce)(s)}catch{throw new d(`Could not resolve ${s}`,"unresolvable")}if(!o.length)throw new d(`Could not resolve ${s}`,"unresolvable");if(o.some(j))throw new d("The URL points at a private or local address","private");return{parsed:n,addresses:o}}var Ne=new Set([301,302,303,307,308]),Ke=["authorization","cookie","proxy-authorization"];function Me(t,e,r){if(!e)return[t.href,r];let n=new URL(t.href);n.hostname=U(e)===6?`[${e}]`:e;let s=new Headers(r.headers);s.set("Host",t.host);let o={...r,headers:s};if(t.protocol==="https:")o.tls={serverName:t.hostname.replace(/^\[|\]$/g,"")};return[n.href,o]}async function Z(t){await t.body?.cancel().catch(()=>{})}async function Oe(t,e){e.throwIfAborted();let r=()=>{},n=new Promise((s,o)=>{r=()=>o(e.reason),e.addEventListener("abort",r,{once:!0})});try{return await Promise.race([Promise.resolve().then(t),n])}finally{e.removeEventListener("abort",r)}}async function J(t,e){let r=Number(t.headers.get("content-length")??Number.NaN);if(Number.isFinite(r)&&r>e)throw await Z(t),new d(`The response is ${r} bytes, over the ${e} byte limit`,"too-large");if(!t.body)return t;let n=[],s=0,o=t.body.getReader();try{for(;;){let{done:c,value:u}=await o.read();if(c)break;if(s+=u.byteLength,s>e)throw await o.cancel(),new d(`The response is over the ${e} byte limit`,"too-large");n.push(u)}}finally{o.releaseLock()}let i=new Uint8Array(s),a=0;for(let c of n)i.set(c,a),a+=c.byteLength;return new Response(i,{status:t.status,statusText:t.statusText,headers:t.headers})}async function dt(t,e={},r={}){let n=r.maxRedirects??3,s=r.maxBytes??5242880,o=r.timeoutMs??1e4,i=AbortSignal.timeout(o),a=e.signal?AbortSignal.any([e.signal,i]):i,c=e.body==null||typeof e.body==="string"||e.body instanceof Uint8Array||e.body instanceof ArrayBuffer,u=t,g={...e,signal:a,redirect:"manual"},b="";for(let x=0;;x++){a.throwIfAborted();let{parsed:A,addresses:te}=await Oe(()=>Q(u,r),a);if(b&&A.origin!==b){let v=new Headers(g.headers);for(let T of Ke)v.delete(T);g={...g,headers:v}}b=A.origin;let[re,ne]=Me(A,te[0],g),h=await fetch(re,ne);if(!Ne.has(h.status))return await J(h,s);let N=h.headers.get("location");if(!N)return await J(h,s);if(await Z(h),x>=n)throw new d(`The URL redirected more than ${n} times`,"redirects");if(h.status!==303&&g.body!=null&&!c)throw new d("The URL redirected, and a streaming request body cannot be sent again","unreplayable-body");let P=(g.method??"GET").toUpperCase();if((h.status===301||h.status===302)&&P==="POST"||h.status===303&&P!=="GET"&&P!=="HEAD"){let v=new Headers(g.headers);for(let T of["content-encoding","content-language","content-location","content-type","content-length"])v.delete(T);g={...g,headers:v,method:"GET",body:void 0}}u=new URL(N,A).href}}import{devNull as Le}from"os";class ee extends Error{constructor(){super("Regular expression exceeded its execution budget");this.name="RegexTimeoutError"}}var Ie=`
|
|
3
3
|
const { source, flags, input } = JSON.parse(await Bun.stdin.text());
|
|
4
4
|
try {
|
|
5
5
|
const match = new RegExp(source, flags).exec(input);
|
|
@@ -7,4 +7,4 @@ try {
|
|
|
7
7
|
} catch {
|
|
8
8
|
console.log(JSON.stringify({ invalid: true }));
|
|
9
9
|
}
|
|
10
|
-
`;function
|
|
10
|
+
`;function wt(t,e,r={}){let n=r.timeoutMs??25;if(!Number.isInteger(n)||n<1||n>1000)throw RangeError("Regex timeout must be an integer between 1 and 1000 milliseconds");if(typeof t!=="string"||t.length>8192||typeof e!=="string"||e.length>65536)throw RangeError("Regex source or input exceeds its size limit");if(r.flags!==void 0&&typeof r.flags!=="string")throw TypeError("Regex flags must be a string");if(r.flags!==void 0&&r.flags.length>8)throw SyntaxError("Invalid regular expression flags");let s=Bun.which("bun");if(!s)throw Error("Bounded regex matching requires the Bun executable on PATH");let o=Bun.spawnSync([s,`--config=${Le}`,"--no-env-file","--no-install","--eval",Ie],{env:{},stdin:Buffer.from(JSON.stringify({source:t,flags:r.flags??"",input:e})),stdout:"pipe",stderr:"ignore",timeout:n,killSignal:"SIGKILL",maxBuffer:524288});if(o.signalCode==="SIGKILL")throw new ee;if(o.exitCode!==0)throw Error("Isolated regex execution failed");let i=JSON.parse(o.stdout.toString());if(i.invalid)throw SyntaxError("Invalid regular expression");if(i.match===null)return null;if(!i.match||typeof i.match.value!=="string"||!Number.isInteger(i.match.index))throw Error("Invalid isolated regex response");return i.match}export{l as InvalidWebhookSignature,d as OutboundUrlError,ee as RegexTimeoutError,Ee as apiKeyHint,he as argon2Encode,ht as assertPublicOutboundUrl,Je as base64Encode,ge as bcryptEncode,Qe as check,qe as decrypt,W as detectAlgorithm,De as encrypt,dt as fetchPublicUrl,mt as generateApiKey,rt as generateAppKey,it as generateWebhookSecret,Se as hashApiKey,Qe as hashCheck,W as hashDetectAlgorithm,fe as hashInfo,je as hashMake,Xe as hashNeedsRehash,fe as info,j as isPrivateAddress,Ge as legacyEncrypt,je as make,je as makeHash,wt as matchBoundedRegex,Xe as needsRehash,ke as outboundUrlProblem,ot as signWebhook,Ze as timingSafeEqualString,ve as verifyGithub,Qe as verifyHash,Ae as verifyHmac,xe as verifySignedWebhook,be as verifyStripe,at as verifyWebhook};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/security",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.257",
|
|
6
6
|
"description": "The Stacks framework security.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
"prepublishOnly": "bun run build"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@stacksjs/config": "0.75.
|
|
60
|
+
"@stacksjs/config": "0.75.257",
|
|
61
61
|
"ts-security-crypto": "0.0.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@stacksjs/env": "0.75.
|
|
65
|
-
"@stacksjs/types": "0.75.
|
|
66
|
-
"@stacksjs/validation": "0.75.
|
|
64
|
+
"@stacksjs/env": "0.75.257",
|
|
65
|
+
"@stacksjs/types": "0.75.257",
|
|
66
|
+
"@stacksjs/validation": "0.75.257",
|
|
67
67
|
"better-dx": "^0.2.24"
|
|
68
68
|
}
|
|
69
69
|
}
|