aip-master-node-sumit 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +147 -50
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const axios = require('axios');
2
2
 
3
- // 🛡️ THE FIX: Smart URL Cleaner ensures the base API URL is exactly right
3
+ // 🛡️ Smart URL Cleaner
4
4
  const getSafeMasterUrl = (url) => {
5
5
  if (!url) return 'http://localhost:8080/api/v1';
6
6
  let clean = url.replace(/\/+$/, '');
@@ -10,6 +10,22 @@ const getSafeMasterUrl = (url) => {
10
10
  return clean;
11
11
  };
12
12
 
13
+ // 🛡️ THE FIX: Bulletproof Native Cookie Parser (Handles JWT padding '=')
14
+ const parseRawCookies = (cookieHeader) => {
15
+ if (!cookieHeader) return {};
16
+
17
+ return cookieHeader.split(';').reduce((acc, cookie) => {
18
+ const index = cookie.indexOf('=');
19
+ if (index < 0) return acc;
20
+
21
+ const key = decodeURIComponent(cookie.substring(0, index).trim());
22
+ const val = decodeURIComponent(cookie.substring(index + 1).trim());
23
+
24
+ acc[key] = val;
25
+ return acc;
26
+ }, {});
27
+ };
28
+
13
29
  const AIP_MASTER_API = getSafeMasterUrl(process.env.AIP_MASTER_URL);
14
30
  const API_KEY = process.env.AIP_MASTER_API_KEY;
15
31
 
@@ -24,7 +40,6 @@ if (API_KEY) {
24
40
  // Silently fail so the client's app doesn't crash if Master API is updating
25
41
  }
26
42
  };
27
-
28
43
  sendHeartbeat();
29
44
  setInterval(sendHeartbeat, 5 * 60 * 1000);
30
45
  }
@@ -38,11 +53,9 @@ const aipGuard = (options = { requireLogin: true }) => {
38
53
  const origin = req.headers.origin || req.headers.Origin;
39
54
 
40
55
  if (origin) {
41
- // If origin is present, dynamically echo it to satisfy strict browser credential policies
42
56
  res.setHeader('Access-Control-Allow-Origin', origin);
43
57
  res.setHeader('Access-Control-Allow-Credentials', 'true');
44
58
  } else {
45
- // Fallback for non-browser clients (like Postman or cURL)
46
59
  res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
47
60
  }
48
61
 
@@ -65,12 +78,13 @@ const aipGuard = (options = { requireLogin: true }) => {
65
78
  if (req.headers['transfer-encoding'] && req.headers['transfer-encoding'].includes('chunked')) {
66
79
  return res.status(411).json({ error: "AIP Infrastructure Shield: Chunked encoding rejected." });
67
80
  }
68
-
69
81
  if (req.headers['content-length'] && parseInt(req.headers['content-length']) > MAX_PAYLOAD_BYTES) {
70
82
  return res.status(413).json({ error: "AIP Infrastructure Shield: Payload Too Large." });
71
83
  }
72
84
 
73
- let token = req.query.token || (req.cookies && req.cookies.aip_session);
85
+ // 🛡️ THE FIX: Smart Cookie Extraction & Removed Vulnerable Query Tokens
86
+ const cookies = req.cookies || parseRawCookies(req.headers.cookie);
87
+ let token = cookies.aip_session;
74
88
 
75
89
  if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
76
90
  token = req.headers.authorization.split(' ')[1];
@@ -82,11 +96,13 @@ const aipGuard = (options = { requireLogin: true }) => {
82
96
  // 🛡️ LAYER 2: IDENTITY CHECK & RBAC
83
97
  // ==========================================
84
98
  if (options.requireLogin) {
85
- if (!token) return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
99
+ if (!token) {
100
+ return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
101
+ }
86
102
 
87
103
  try {
88
104
  const currentAction = req.method + " " + (req.baseUrl + req.path);
89
-
105
+
90
106
  const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
91
107
  { session_token: token, action: currentAction },
92
108
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
@@ -94,15 +110,39 @@ const aipGuard = (options = { requireLogin: true }) => {
94
110
 
95
111
  req.user = authRes.data.user;
96
112
 
113
+ // 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
97
114
  const isProd = process.env.NODE_ENV === 'production';
98
- res.cookie('aip_session', token, { httpOnly: true, secure: isProd });
115
+
116
+ if (typeof res.cookie === 'function') {
117
+ // Express.js environment
118
+ res.cookie('aip_session', token, {
119
+ httpOnly: true,
120
+ secure: isProd,
121
+ sameSite: isProd ? 'none' : 'lax',
122
+ path: '/',
123
+ maxAge: 12 * 60 * 60 * 1000
124
+ });
125
+ } else {
126
+ // Raw Node.js or alternate framework environment
127
+ const cookieStr = `aip_session=${token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`;
128
+ res.setHeader('Set-Cookie', cookieStr);
129
+ }
99
130
 
100
131
  } catch (error) {
101
132
  const status = error.response?.status || 500;
102
133
  const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
134
+ const isProd = process.env.NODE_ENV === 'production';
103
135
 
104
- if (status === 401 && res.clearCookie) {
105
- res.clearCookie('aip_session');
136
+ if (status === 401) {
137
+ if (typeof res.clearCookie === 'function') {
138
+ res.clearCookie('aip_session', {
139
+ path: '/',
140
+ sameSite: isProd ? 'none' : 'lax',
141
+ secure: isProd
142
+ });
143
+ } else {
144
+ res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
145
+ }
106
146
  }
107
147
 
108
148
  return res.status(status === 401 ? 401 : 403).json({ error: errMsg });
@@ -115,12 +155,12 @@ const aipGuard = (options = { requireLogin: true }) => {
115
155
  try {
116
156
  const payloadData = JSON.stringify({
117
157
  body: req.body || {},
118
- query: req.query || {}
158
+ query: req.query || {}
119
159
  });
120
-
160
+
121
161
  const forwardedFor = req.headers['x-forwarded-for'];
122
162
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
123
-
163
+
124
164
  if (forwardedFor) {
125
165
  clientIp = Array.isArray(forwardedFor)
126
166
  ? forwardedFor[0].trim()
@@ -128,12 +168,12 @@ const aipGuard = (options = { requireLogin: true }) => {
128
168
  }
129
169
 
130
170
  const wafRes = await axios.post(`${AIP_MASTER_API}/verify`, {
131
- api_key: API_KEY,
132
- ip: clientIp,
133
- method: req.method,
171
+ api_key: API_KEY,
172
+ ip: clientIp,
173
+ method: req.method,
134
174
  url: req.originalUrl,
135
- payload: payloadData,
136
- origin: req.headers.origin || "",
175
+ payload: payloadData,
176
+ origin: req.headers.origin || "",
137
177
  user_agent: req.headers['user-agent'] || "",
138
178
  session_token: token || ""
139
179
  });
@@ -143,68 +183,125 @@ const aipGuard = (options = { requireLogin: true }) => {
143
183
  }
144
184
 
145
185
  } catch (error) {
146
- console.error("WAF Engine Unreachable");
147
186
  return res.status(500).json({ error: "Security verification failed." });
148
187
  }
149
-
188
+
150
189
  next();
151
190
  };
152
191
  };
153
192
 
154
193
  // ==========================================
155
- // 🛡️ NEW: IaaS Admin Client SDK
194
+ // 🛡️ Admin Client SDK
156
195
  // ==========================================
157
196
  class AipAdminClient {
158
197
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
159
198
  if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
160
199
  this.apiKey = apiKey;
161
200
  this.baseUrl = getSafeMasterUrl(masterUrl);
162
-
163
- this.client = axios.create({
164
- baseURL: this.baseUrl,
165
- headers: { 'Authorization': `Bearer ${this.apiKey}` }
201
+ this.client = axios.create({
202
+ baseURL: this.baseUrl,
203
+ headers: { 'Authorization': `Bearer ${this.apiKey}` }
166
204
  });
167
205
  }
168
206
 
169
- // --- User Management ---
170
- async getUsers() {
171
- const res = await this.client.get('/sdk/admin/users');
172
- return res.data;
207
+ async getUsers() {
208
+ const res = await this.client.get('/sdk/admin/users');
209
+ return res.data;
173
210
  }
174
211
 
175
- async createUser({ name, email, password, role_id, send_email = false }) {
176
- const res = await this.client.post('/sdk/admin/users', { name, email, password, role_id, send_email });
177
- return res.data;
212
+ async createUser(data) {
213
+ const res = await this.client.post('/sdk/admin/users', data);
214
+ return res.data;
178
215
  }
179
216
 
180
- async updateUser(userId, { name, role_id }) {
181
- const res = await this.client.put(`/sdk/admin/users/${userId}`, { name, role_id });
182
- return res.data;
217
+ async updateUser(userId, data) {
218
+ const res = await this.client.put(`/sdk/admin/users/${userId}`, data);
219
+ return res.data;
183
220
  }
184
221
 
185
- async updateRBAC(userId, permissions) {
186
- const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
187
- return res.data;
222
+ async updateRBAC(userId, permissions) {
223
+ const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
224
+ return res.data;
188
225
  }
189
226
 
190
- async deleteUser(userId) {
191
- const res = await this.client.delete(`/sdk/admin/users/${userId}`);
192
- return res.data;
227
+ async deleteUser(userId) {
228
+ const res = await this.client.delete(`/sdk/admin/users/${userId}`);
229
+ return res.data;
193
230
  }
194
231
 
195
- // --- Role Management ---
196
- async getRoles() {
197
- const res = await this.client.get('/sdk/admin/roles');
198
- return res.data;
232
+ async getRoles() {
233
+ const res = await this.client.get('/sdk/admin/roles');
234
+ return res.data;
199
235
  }
200
236
 
201
- async createRole({ name, slug, permissions }) {
202
- const res = await this.client.post('/sdk/admin/roles', { name, slug, permissions });
203
- return res.data;
237
+ async createRole(data) {
238
+ const res = await this.client.post('/sdk/admin/roles', data);
239
+ return res.data;
240
+ }
241
+ }
242
+
243
+ // ==========================================
244
+ // 🛡️ End-User IAM Client SDK
245
+ // ==========================================
246
+ class AipIAMClient {
247
+ constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
248
+ if (!apiKey) throw new Error("AIP IAM Client requires an API Key");
249
+ this.apiKey = apiKey;
250
+ this.baseUrl = getSafeMasterUrl(masterUrl);
251
+ this.client = axios.create({ baseURL: this.baseUrl });
252
+ }
253
+
254
+ async loginUser(req, res, credentials) {
255
+ let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
256
+ const forwardedFor = req.headers['x-forwarded-for'];
257
+
258
+ if (forwardedFor) {
259
+ clientIp = Array.isArray(forwardedFor)
260
+ ? forwardedFor[0].trim()
261
+ : forwardedFor.split(',')[0].trim();
262
+ }
263
+
264
+ const authRes = await this.client.post('/iam/auth', {
265
+ api_key: this.apiKey,
266
+ ip: clientIp,
267
+ ...credentials
268
+ });
269
+
270
+ if (authRes.data.session_token) {
271
+ const isProd = process.env.NODE_ENV === 'production';
272
+
273
+ if (typeof res.cookie === 'function') {
274
+ res.cookie('aip_session', authRes.data.session_token, {
275
+ httpOnly: true,
276
+ secure: isProd,
277
+ sameSite: isProd ? 'none' : 'lax',
278
+ path: '/',
279
+ maxAge: 12 * 60 * 60 * 1000
280
+ });
281
+ } else {
282
+ res.setHeader('Set-Cookie', `aip_session=${authRes.data.session_token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`);
283
+ }
284
+ }
285
+ return authRes.data;
286
+ }
287
+
288
+ logoutUser(res) {
289
+ const isProd = process.env.NODE_ENV === 'production';
290
+
291
+ if (typeof res.clearCookie === 'function') {
292
+ res.clearCookie('aip_session', {
293
+ path: '/',
294
+ sameSite: isProd ? 'none' : 'lax',
295
+ secure: isProd
296
+ });
297
+ } else {
298
+ res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
299
+ }
204
300
  }
205
301
  }
206
302
 
207
- // 🛡️ Attach the Admin Client directly to the Guard export for backward compatibility
303
+ // Attach the clients directly to the Guard export for easy importing
208
304
  aipGuard.AipAdminClient = AipAdminClient;
305
+ aipGuard.AipIAMClient = AipIAMClient;
209
306
 
210
307
  module.exports = aipGuard;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aip-master-node-sumit",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Enterprise-grade WAF and IAM security middleware for Node.js.",
5
5
  "main": "index.js",
6
6
  "scripts": {