post-armor 1.0.0 → 1.0.1
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/post-armor.js +1 -201
- package/package.json +1 -1
package/dist/post-armor.js
CHANGED
|
@@ -1,201 +1 @@
|
|
|
1
|
-
const LIB_VERSION =
|
|
2
|
-
export function getTimestamp() {
|
|
3
|
-
const date = new Date();
|
|
4
|
-
return date
|
|
5
|
-
.toLocaleTimeString("en-GB", {
|
|
6
|
-
timeZone: "Asia/Kolkata",
|
|
7
|
-
hour12: false,
|
|
8
|
-
hour: "2-digit",
|
|
9
|
-
minute: "2-digit",
|
|
10
|
-
second: "2-digit",
|
|
11
|
-
})
|
|
12
|
-
.replace(/:/g, ""); // Returns "HHmmss"
|
|
13
|
-
}
|
|
14
|
-
function encryptTime(time, key) {
|
|
15
|
-
let encrypted = "";
|
|
16
|
-
for (let i = 0; i < time.length; i++) {
|
|
17
|
-
const charCode = time.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
|
18
|
-
encrypted += String.fromCharCode(charCode);
|
|
19
|
-
}
|
|
20
|
-
const random1 = Math.random().toString(36).substring(2, 7);
|
|
21
|
-
const random2 = Math.random().toString(36).substring(2, 7);
|
|
22
|
-
// Reverse the XOR result
|
|
23
|
-
encrypted = encrypted.split("").reverse().join("");
|
|
24
|
-
// Combine with random salts and encode to Base64 (stripping padding)
|
|
25
|
-
return btoa(random1 + encrypted + random2).replace(/=/g, "");
|
|
26
|
-
}
|
|
27
|
-
function decryptTime(raw, key) {
|
|
28
|
-
if (raw.length < 10)
|
|
29
|
-
return "";
|
|
30
|
-
let encrypted = raw.substring(5, raw.length - 5);
|
|
31
|
-
encrypted = encrypted.split("").reverse().join("");
|
|
32
|
-
let decrypted = "";
|
|
33
|
-
for (let i = 0; i < encrypted.length; i++) {
|
|
34
|
-
decrypted += String.fromCharCode(encrypted.charCodeAt(i) ^ key.charCodeAt(i % key.length));
|
|
35
|
-
}
|
|
36
|
-
return decrypted;
|
|
37
|
-
}
|
|
38
|
-
export function getSecureToken(key) {
|
|
39
|
-
const time = getTimestamp();
|
|
40
|
-
return encryptTime(time, key);
|
|
41
|
-
}
|
|
42
|
-
export function validateToken(token, key, delay = 5) {
|
|
43
|
-
try {
|
|
44
|
-
if (!token)
|
|
45
|
-
return false;
|
|
46
|
-
// Restore Base64 padding if missing
|
|
47
|
-
const paddedToken = token.padEnd(token.length + ((4 - (token.length % 4)) % 4), "=");
|
|
48
|
-
const raw = atob(paddedToken);
|
|
49
|
-
const decryptedTime = decryptTime(raw, key);
|
|
50
|
-
if (decryptedTime.length !== 6)
|
|
51
|
-
return false;
|
|
52
|
-
const serverTime = getTimestamp();
|
|
53
|
-
const toSeconds = (t) => {
|
|
54
|
-
const h = parseInt(t.substring(0, 2), 10);
|
|
55
|
-
const m = parseInt(t.substring(2, 4), 10);
|
|
56
|
-
const s = parseInt(t.substring(4, 6), 10);
|
|
57
|
-
return h * 3600 + m * 60 + s;
|
|
58
|
-
};
|
|
59
|
-
const serverSec = toSeconds(serverTime);
|
|
60
|
-
const clientSec = toSeconds(decryptedTime);
|
|
61
|
-
let diff = serverSec - clientSec;
|
|
62
|
-
// Handle midnight wrap-around
|
|
63
|
-
if (diff < -80000)
|
|
64
|
-
diff += 86400;
|
|
65
|
-
if (diff > 80000)
|
|
66
|
-
diff -= 86400;
|
|
67
|
-
return Math.abs(diff) <= delay;
|
|
68
|
-
}
|
|
69
|
-
catch (e) {
|
|
70
|
-
console.error("Token validation error:", e?.message);
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Sends a secure POST request with the given options.
|
|
76
|
-
* @param options The options for the request.
|
|
77
|
-
* @returns A Promise that resolves to the response from the server.
|
|
78
|
-
*/
|
|
79
|
-
export async function armoredPost({ url, headers = {}, body, key, headerName = "post-armor-token", sourceName = "post-armor", }) {
|
|
80
|
-
const _headers = {
|
|
81
|
-
...headers,
|
|
82
|
-
"Content-Type": "application/octet-stream",
|
|
83
|
-
[headerName]: getSecureToken(key),
|
|
84
|
-
};
|
|
85
|
-
const encoder = new TextEncoder();
|
|
86
|
-
const _body = encoder.encode(JSON.stringify({
|
|
87
|
-
body,
|
|
88
|
-
_: {
|
|
89
|
-
source: sourceName,
|
|
90
|
-
version: LIB_VERSION,
|
|
91
|
-
},
|
|
92
|
-
}));
|
|
93
|
-
const response = await fetch(url, {
|
|
94
|
-
method: "POST",
|
|
95
|
-
headers: _headers,
|
|
96
|
-
body: _body,
|
|
97
|
-
});
|
|
98
|
-
let received = {
|
|
99
|
-
body: undefined,
|
|
100
|
-
type: "undefined",
|
|
101
|
-
_: {
|
|
102
|
-
source: "",
|
|
103
|
-
version: "",
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
if (response.status == 200) {
|
|
107
|
-
const buffer = await response.arrayBuffer();
|
|
108
|
-
const decoder = new TextDecoder();
|
|
109
|
-
const text = decoder.decode(buffer);
|
|
110
|
-
try {
|
|
111
|
-
received = JSON.parse(text);
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
throw new Error("Failed to parse armored response");
|
|
115
|
-
}
|
|
116
|
-
if (received._?.source !== sourceName || received._?.version !== LIB_VERSION) {
|
|
117
|
-
throw new Error(`Invalid response source: expected ${sourceName}, got ${received._?.source}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
status: response.status,
|
|
122
|
-
statusText: response.statusText,
|
|
123
|
-
body: received.body || undefined,
|
|
124
|
-
type: typeof received.body,
|
|
125
|
-
ok: response.ok,
|
|
126
|
-
url: response.url,
|
|
127
|
-
redirected: response.redirected,
|
|
128
|
-
headers: response.headers,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Creates a PostArmor middleware instance with the given configuration.
|
|
133
|
-
* This middleware is built using Web Standards and can be safely included in
|
|
134
|
-
* browser bundles without causing errors.
|
|
135
|
-
*/
|
|
136
|
-
export function postArmor(config) {
|
|
137
|
-
const KEY = config.key;
|
|
138
|
-
const MAX_DELAY = config.delay ?? 5;
|
|
139
|
-
const IS_STRICT = config.strict ?? true;
|
|
140
|
-
const HEADER_NAME = (config.headerName || "post-armor-token").toLowerCase();
|
|
141
|
-
const SOURCE_NAME = config.sourceName || "post-armor";
|
|
142
|
-
if (!KEY) {
|
|
143
|
-
throw new Error("PostArmor: config.key is required");
|
|
144
|
-
}
|
|
145
|
-
// Using 'any' for types here to avoid importing 'express' which breaks browser builds.
|
|
146
|
-
// The behavior remains identical when used in an Express app.
|
|
147
|
-
return (req, res, next) => {
|
|
148
|
-
try {
|
|
149
|
-
const token = req.headers[HEADER_NAME];
|
|
150
|
-
if (!validateToken(token, KEY, MAX_DELAY)) {
|
|
151
|
-
console.warn(`[PostArmor] Invalid or expired token for header ${HEADER_NAME}`);
|
|
152
|
-
res.status(401).json({ error: "Unauthorized: Invalid secure token" });
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (!req.body || (req.body instanceof Uint8Array && req.body.length === 0)) {
|
|
156
|
-
res.status(400).json({ error: "Missing request body" });
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
let received;
|
|
160
|
-
try {
|
|
161
|
-
// Buffer is replaced by Uint8Array and TextDecoder for universal compatibility
|
|
162
|
-
const decodedBody = typeof req.body === "string"
|
|
163
|
-
? req.body
|
|
164
|
-
: (req.body instanceof Uint8Array || (typeof Buffer !== "undefined" && Buffer.isBuffer(req.body)))
|
|
165
|
-
? new TextDecoder().decode(req.body)
|
|
166
|
-
: JSON.stringify(req.body);
|
|
167
|
-
received = JSON.parse(decodedBody);
|
|
168
|
-
}
|
|
169
|
-
catch (e) {
|
|
170
|
-
res.status(400).json({ error: "Malformed request payload" });
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
if (!received._ || received._.source !== SOURCE_NAME) {
|
|
174
|
-
res.status(400).json({ error: "Invalid request source" });
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
// Replace body with actual payload
|
|
178
|
-
req.body = received.body;
|
|
179
|
-
// Custom responder
|
|
180
|
-
res.return = (type, body) => {
|
|
181
|
-
if (IS_STRICT && type !== "any" && typeof body !== type) {
|
|
182
|
-
throw new Error(`Invalid response type: expected ${type}, got ${typeof body}`);
|
|
183
|
-
}
|
|
184
|
-
const responsePayload = JSON.stringify({
|
|
185
|
-
body,
|
|
186
|
-
_: {
|
|
187
|
-
source: SOURCE_NAME,
|
|
188
|
-
version: LIB_VERSION,
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
// Use TextEncoder to return a Uint8Array (compatible with Express res.send)
|
|
192
|
-
res.send(new TextEncoder().encode(responsePayload));
|
|
193
|
-
};
|
|
194
|
-
next();
|
|
195
|
-
}
|
|
196
|
-
catch (error) {
|
|
197
|
-
console.error(`[PostArmor] Error processing request on ${req.path}:`, error?.message);
|
|
198
|
-
res.status(400).json({ error: "Internal processing error" });
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
}
|
|
1
|
+
const PA0_0x99efa7=PA0_0x4301;(function(_0x195680,_0x353f06){const _0x1965f0=PA0_0x4301,_0x33c9c8=_0x195680();while(!![]){try{const _0x54edcf=-parseInt(_0x1965f0(0x147))/0x1*(parseInt(_0x1965f0(0x129))/0x2)+-parseInt(_0x1965f0(0x134))/0x3*(parseInt(_0x1965f0(0x128))/0x4)+parseInt(_0x1965f0(0x138))/0x5+-parseInt(_0x1965f0(0x15b))/0x6+-parseInt(_0x1965f0(0x130))/0x7*(parseInt(_0x1965f0(0x151))/0x8)+parseInt(_0x1965f0(0x13a))/0x9*(parseInt(_0x1965f0(0x132))/0xa)+parseInt(_0x1965f0(0x144))/0xb;if(_0x54edcf===_0x353f06)break;else _0x33c9c8['push'](_0x33c9c8['shift']());}catch(_0xc7f508){_0x33c9c8['push'](_0x33c9c8['shift']());}}}(PA0_0x236c,0x8abb1));const LIB_VERSION=PA0_0x99efa7(0x143);export function getTimestamp(){const _0x93af31=PA0_0x99efa7,_0x28688e=new Date();return _0x28688e[_0x93af31(0x160)](_0x93af31(0x13c),{'timeZone':_0x93af31(0x149),'hour12':![],'hour':'2-digit','minute':_0x93af31(0x14c),'second':_0x93af31(0x14c)})['replace'](/:/g,'');}function PA0_0x236c(){const _0x46746d=['Asia/Kolkata','charCodeAt','delay','2-digit','error','post-armor-token','length','url','235256gtPLSf','toString','Invalid\x20response\x20type:\x20expected\x20','split','encode','send',',\x20got\x20','parse','application/octet-stream','Failed\x20to\x20parse\x20armored\x20response','2847036Eqrmft','stringify','replace','substring','return','toLocaleTimeString','decode','sourceName','padEnd','join','message','908xCEduq','1022mNdPwF','source','toLowerCase','abs','body','warn','headers','259cBFVCy','Invalid\x20response\x20source:\x20expected\x20','1762990TMiQpO','[PostArmor]\x20Error\x20processing\x20request\x20on\x20','3765UfHsqn','PostArmor:','POST','undefined','369805eGyiMs','post-armor','45KTRWum','PostArmor:\x20config.key\x20is\x20required','en-GB','isBuffer','Invalid\x20request\x20source','reverse','strict','random','path','1.0.0','19946696ddPWaD','json','status','691gmtySn','Token\x20validation\x20error:'];PA0_0x236c=function(){return _0x46746d;};return PA0_0x236c();}function encryptTime(_0x5e85e0,_0x4d6615){const _0x180593=PA0_0x99efa7;let _0x448572='';for(let _0x2d7619=0x0;_0x2d7619<_0x5e85e0[_0x180593(0x14f)];_0x2d7619++){const _0x374d68=_0x5e85e0[_0x180593(0x14a)](_0x2d7619)^_0x4d6615[_0x180593(0x14a)](_0x2d7619%_0x4d6615[_0x180593(0x14f)]);_0x448572+=String['fromCharCode'](_0x374d68);}const _0xd53bc5=Math['random']()[_0x180593(0x152)](0x24)['substring'](0x2,0x7),_0xa35a24=Math[_0x180593(0x141)]()[_0x180593(0x152)](0x24)[_0x180593(0x15e)](0x2,0x7);return _0x448572=_0x448572[_0x180593(0x154)]('')[_0x180593(0x13f)]()['join'](''),btoa(_0xd53bc5+_0x448572+_0xa35a24)[_0x180593(0x15d)](/=/g,'');}function decryptTime(_0x234f31,_0x22431f){const _0x469a9d=PA0_0x99efa7;if(_0x234f31[_0x469a9d(0x14f)]<0xa)return'';let _0x2ca3a1=_0x234f31[_0x469a9d(0x15e)](0x5,_0x234f31['length']-0x5);_0x2ca3a1=_0x2ca3a1[_0x469a9d(0x154)]('')[_0x469a9d(0x13f)]()[_0x469a9d(0x126)]('');let _0x889fdc='';for(let _0x4d365f=0x0;_0x4d365f<_0x2ca3a1['length'];_0x4d365f++){_0x889fdc+=String['fromCharCode'](_0x2ca3a1[_0x469a9d(0x14a)](_0x4d365f)^_0x22431f[_0x469a9d(0x14a)](_0x4d365f%_0x22431f[_0x469a9d(0x14f)]));}return _0x889fdc;}export function getSecureToken(_0x26dac8){const _0x8b78ce=getTimestamp();return encryptTime(_0x8b78ce,_0x26dac8);}export function validateToken(_0xc6d012,_0x504c18,_0x200e7b=0x5){const _0x53adbc=PA0_0x99efa7;try{if(!_0xc6d012)return![];const _0x374ab6=_0xc6d012[_0x53adbc(0x125)](_0xc6d012[_0x53adbc(0x14f)]+(0x4-_0xc6d012[_0x53adbc(0x14f)]%0x4)%0x4,'='),_0x18160c=atob(_0x374ab6),_0x269fe8=decryptTime(_0x18160c,_0x504c18);if(_0x269fe8[_0x53adbc(0x14f)]!==0x6)return![];const _0x4ca66d=getTimestamp(),_0x575fa8=_0x37f56b=>{const _0x1f9765=_0x53adbc,_0x580f2c=parseInt(_0x37f56b['substring'](0x0,0x2),0xa),_0x6ea4c6=parseInt(_0x37f56b[_0x1f9765(0x15e)](0x2,0x4),0xa),_0x46829=parseInt(_0x37f56b[_0x1f9765(0x15e)](0x4,0x6),0xa);return _0x580f2c*0xe10+_0x6ea4c6*0x3c+_0x46829;},_0x48cd26=_0x575fa8(_0x4ca66d),_0x5a13e6=_0x575fa8(_0x269fe8);let _0x123198=_0x48cd26-_0x5a13e6;if(_0x123198<-0x13880)_0x123198+=0x15180;if(_0x123198>0x13880)_0x123198-=0x15180;return Math[_0x53adbc(0x12c)](_0x123198)<=_0x200e7b;}catch(_0x15b275){return console['error'](_0x53adbc(0x148),_0x15b275?.[_0x53adbc(0x127)]),![];}}const _preEncoder=PA0_0x99efa7(0x135);function PA0_0x4301(_0xd3e5e0,_0x2c254a){_0xd3e5e0=_0xd3e5e0-0x124;const _0x236c06=PA0_0x236c();let _0x430167=_0x236c06[_0xd3e5e0];return _0x430167;}function encodeBody(_0x553c3d){const _0x5f1629=PA0_0x99efa7;return btoa(_preEncoder+JSON[_0x5f1629(0x15c)](_0x553c3d));}function decodeBody(_0x3a83fb){const _0x51e877=PA0_0x99efa7;return JSON[_0x51e877(0x158)](atob(_0x3a83fb)[_0x51e877(0x15e)](_preEncoder[_0x51e877(0x14f)]));}export async function armoredPost({url:_0x504887,headers:headers={},body:_0x27cbe7,key:_0x5a4341,headerName:headerName=PA0_0x99efa7(0x14e),sourceName:sourceName='post-armor'}){const _0xc59596=PA0_0x99efa7,_0x14cb9b={...headers,'Content-Type':_0xc59596(0x159),[headerName]:getSecureToken(_0x5a4341)},_0x1b7d64=new TextEncoder(),_0xb776bd=_0x1b7d64['encode'](encodeBody(JSON[_0xc59596(0x15c)]({'body':_0x27cbe7,'_':{'source':sourceName,'version':LIB_VERSION}}))),_0x17237b=await fetch(_0x504887,{'method':_0xc59596(0x136),'headers':_0x14cb9b,'body':_0xb776bd});let _0x1f206f={'body':undefined,'type':_0xc59596(0x137),'_':{'source':'','version':''}};if(_0x17237b[_0xc59596(0x146)]==0xc8){const _0x581dae=await _0x17237b['arrayBuffer'](),_0x671481=new TextDecoder(),_0x435d1f=_0x671481[_0xc59596(0x161)](_0x581dae);try{_0x1f206f=JSON[_0xc59596(0x158)](_0x435d1f);}catch(_0x1483b4){throw new Error(_0xc59596(0x15a));}if(_0x1f206f['_']?.[_0xc59596(0x12a)]!==sourceName||_0x1f206f['_']?.['version']!==LIB_VERSION)throw new Error(_0xc59596(0x131)+sourceName+',\x20got\x20'+_0x1f206f['_']?.[_0xc59596(0x12a)]);}return{'status':_0x17237b['status'],'statusText':_0x17237b['statusText'],'body':_0x1f206f[_0xc59596(0x12d)]||undefined,'type':typeof _0x1f206f['body'],'ok':_0x17237b['ok'],'url':_0x17237b[_0xc59596(0x150)],'redirected':_0x17237b['redirected'],'headers':_0x17237b[_0xc59596(0x12f)]};}export function postArmor(_0x12ba4c){const _0x4cb066=PA0_0x99efa7,_0x5833bb=_0x12ba4c['key'],_0x1b0dce=_0x12ba4c[_0x4cb066(0x14b)]??0x5,_0xffd34a=_0x12ba4c[_0x4cb066(0x140)]??!![],_0x3cdafb=(_0x12ba4c['headerName']||_0x4cb066(0x14e))[_0x4cb066(0x12b)](),_0x158a0d=_0x12ba4c[_0x4cb066(0x124)]||_0x4cb066(0x139);if(!_0x5833bb)throw new Error(_0x4cb066(0x13b));return(_0x4b3abd,_0x9782a0,_0x20c104)=>{const _0x7f8998=_0x4cb066;try{const _0x1e41f4=_0x4b3abd[_0x7f8998(0x12f)][_0x3cdafb];if(!validateToken(_0x1e41f4,_0x5833bb,_0x1b0dce)){console[_0x7f8998(0x12e)]('[PostArmor]\x20Invalid\x20or\x20expired\x20token\x20for\x20header\x20'+_0x3cdafb),_0x9782a0[_0x7f8998(0x146)](0x191)[_0x7f8998(0x145)]({'error':'Unauthorized:\x20Invalid\x20secure\x20token'});return;}if(!_0x4b3abd[_0x7f8998(0x12d)]||_0x4b3abd['body']instanceof Uint8Array&&_0x4b3abd[_0x7f8998(0x12d)][_0x7f8998(0x14f)]===0x0){_0x9782a0['status'](0x190)[_0x7f8998(0x145)]({'error':'Missing\x20request\x20body'});return;}let _0xd0c52;try{const _0x25c268=typeof _0x4b3abd[_0x7f8998(0x12d)]==='string'?_0x4b3abd[_0x7f8998(0x12d)]:_0x4b3abd[_0x7f8998(0x12d)]instanceof Uint8Array||typeof Buffer!=='undefined'&&Buffer[_0x7f8998(0x13d)](_0x4b3abd['body'])?decodeBody(new TextDecoder()[_0x7f8998(0x161)](_0x4b3abd[_0x7f8998(0x12d)])):JSON[_0x7f8998(0x15c)](_0x4b3abd[_0x7f8998(0x12d)]);_0xd0c52=JSON[_0x7f8998(0x158)](_0x25c268);}catch(_0x486f75){_0x9782a0[_0x7f8998(0x146)](0x190)['json']({'error':'Malformed\x20request\x20payload'});return;}if(!_0xd0c52['_']||_0xd0c52['_']['source']!==_0x158a0d){_0x9782a0[_0x7f8998(0x146)](0x190)[_0x7f8998(0x145)]({'error':_0x7f8998(0x13e)});return;}_0x4b3abd[_0x7f8998(0x12d)]=_0xd0c52['body'],_0x9782a0[_0x7f8998(0x15f)]=(_0x483946,_0x26b338)=>{const _0x50fec8=_0x7f8998;if(_0xffd34a&&_0x483946!=='any'&&typeof _0x26b338!==_0x483946)throw new Error(_0x50fec8(0x153)+_0x483946+_0x50fec8(0x157)+typeof _0x26b338);const _0x55c67f=JSON[_0x50fec8(0x15c)]({'body':_0x26b338,'_':{'source':_0x158a0d,'version':LIB_VERSION}});_0x9782a0[_0x50fec8(0x156)](new TextEncoder()[_0x50fec8(0x155)](_0x55c67f));},_0x20c104();}catch(_0x9ad975){console[_0x7f8998(0x14d)](_0x7f8998(0x133)+_0x4b3abd[_0x7f8998(0x142)]+':',_0x9ad975?.['message']),_0x9782a0['status'](0x190)[_0x7f8998(0x145)]({'error':'Internal\x20processing\x20error'});}};}
|
package/package.json
CHANGED