aip-master-node-sumit 1.0.7 → 1.0.8

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 +79 -74
  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
 
@@ -20,11 +36,8 @@ if (API_KEY) {
20
36
  headers: { 'Authorization': `Bearer ${API_KEY}` },
21
37
  timeout: 5000
22
38
  });
23
- } catch (err) {
24
- // Silently fail so the client's app doesn't crash if Master API is updating
25
- }
39
+ } catch (err) {}
26
40
  };
27
-
28
41
  sendHeartbeat();
29
42
  setInterval(sendHeartbeat, 5 * 60 * 1000);
30
43
  }
@@ -38,11 +51,9 @@ const aipGuard = (options = { requireLogin: true }) => {
38
51
  const origin = req.headers.origin || req.headers.Origin;
39
52
 
40
53
  if (origin) {
41
- // If origin is present, dynamically echo it to satisfy strict browser credential policies
42
54
  res.setHeader('Access-Control-Allow-Origin', origin);
43
55
  res.setHeader('Access-Control-Allow-Credentials', 'true');
44
56
  } else {
45
- // Fallback for non-browser clients (like Postman or cURL)
46
57
  res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
47
58
  }
48
59
 
@@ -50,7 +61,6 @@ const aipGuard = (options = { requireLogin: true }) => {
50
61
  res.setHeader('X-Content-Type-Options', 'nosniff');
51
62
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
52
63
 
53
- // Handle preflight requests gracefully
54
64
  if (req.method === 'OPTIONS') {
55
65
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
56
66
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-AIP-Token, X-Requested-With, Accept');
@@ -65,12 +75,13 @@ const aipGuard = (options = { requireLogin: true }) => {
65
75
  if (req.headers['transfer-encoding'] && req.headers['transfer-encoding'].includes('chunked')) {
66
76
  return res.status(411).json({ error: "AIP Infrastructure Shield: Chunked encoding rejected." });
67
77
  }
68
-
69
78
  if (req.headers['content-length'] && parseInt(req.headers['content-length']) > MAX_PAYLOAD_BYTES) {
70
79
  return res.status(413).json({ error: "AIP Infrastructure Shield: Payload Too Large." });
71
80
  }
72
81
 
73
- let token = req.query.token || (req.cookies && req.cookies.aip_session);
82
+ // 🛡️ THE FIX: Smart Cookie Extraction & Removed Vulnerable Query Tokens
83
+ const cookies = req.cookies || parseRawCookies(req.headers.cookie);
84
+ let token = cookies.aip_session;
74
85
 
75
86
  if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
76
87
  token = req.headers.authorization.split(' ')[1];
@@ -86,7 +97,6 @@ const aipGuard = (options = { requireLogin: true }) => {
86
97
 
87
98
  try {
88
99
  const currentAction = req.method + " " + (req.baseUrl + req.path);
89
-
90
100
  const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
91
101
  { session_token: token, action: currentAction },
92
102
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
@@ -94,15 +104,28 @@ const aipGuard = (options = { requireLogin: true }) => {
94
104
 
95
105
  req.user = authRes.data.user;
96
106
 
107
+ // 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
97
108
  const isProd = process.env.NODE_ENV === 'production';
98
- res.cookie('aip_session', token, { httpOnly: true, secure: isProd });
109
+ if (typeof res.cookie === 'function') {
110
+ res.cookie('aip_session', token, {
111
+ httpOnly: true, secure: isProd, sameSite: isProd ? 'none' : 'lax', path: '/', maxAge: 12 * 60 * 60 * 1000
112
+ });
113
+ } else {
114
+ const cookieStr = `aip_session=${token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`;
115
+ res.setHeader('Set-Cookie', cookieStr);
116
+ }
99
117
 
100
118
  } catch (error) {
101
119
  const status = error.response?.status || 500;
102
120
  const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
103
121
 
104
- if (status === 401 && res.clearCookie) {
105
- res.clearCookie('aip_session');
122
+ const isProd = process.env.NODE_ENV === 'production';
123
+ if (status === 401) {
124
+ if (typeof res.clearCookie === 'function') {
125
+ res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
126
+ } else {
127
+ res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
128
+ }
106
129
  }
107
130
 
108
131
  return res.status(status === 401 ? 401 : 403).json({ error: errMsg });
@@ -113,28 +136,16 @@ const aipGuard = (options = { requireLogin: true }) => {
113
136
  // 🛡️ LAYER 3: WAF THREAT SCAN
114
137
  // ==========================================
115
138
  try {
116
- const payloadData = JSON.stringify({
117
- body: req.body || {},
118
- query: req.query || {}
119
- });
120
-
139
+ const payloadData = JSON.stringify({ body: req.body || {}, query: req.query || {} });
121
140
  const forwardedFor = req.headers['x-forwarded-for'];
122
141
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
123
-
124
142
  if (forwardedFor) {
125
- clientIp = Array.isArray(forwardedFor)
126
- ? forwardedFor[0].trim()
127
- : forwardedFor.split(',')[0].trim();
143
+ clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
128
144
  }
129
145
 
130
146
  const wafRes = await axios.post(`${AIP_MASTER_API}/verify`, {
131
- api_key: API_KEY,
132
- ip: clientIp,
133
- method: req.method,
134
- url: req.originalUrl,
135
- payload: payloadData,
136
- origin: req.headers.origin || "",
137
- user_agent: req.headers['user-agent'] || "",
147
+ api_key: API_KEY, ip: clientIp, method: req.method, url: req.originalUrl,
148
+ payload: payloadData, origin: req.headers.origin || "", user_agent: req.headers['user-agent'] || "",
138
149
  session_token: token || ""
139
150
  });
140
151
 
@@ -143,68 +154,62 @@ const aipGuard = (options = { requireLogin: true }) => {
143
154
  }
144
155
 
145
156
  } catch (error) {
146
- console.error("WAF Engine Unreachable");
147
157
  return res.status(500).json({ error: "Security verification failed." });
148
158
  }
149
-
150
159
  next();
151
160
  };
152
161
  };
153
162
 
154
- // ==========================================
155
- // 🛡️ NEW: IaaS Admin Client SDK
156
- // ==========================================
157
163
  class AipAdminClient {
158
164
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
159
165
  if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
160
166
  this.apiKey = apiKey;
161
167
  this.baseUrl = getSafeMasterUrl(masterUrl);
162
-
163
- this.client = axios.create({
164
- baseURL: this.baseUrl,
165
- headers: { 'Authorization': `Bearer ${this.apiKey}` }
166
- });
167
- }
168
-
169
- // --- User Management ---
170
- async getUsers() {
171
- const res = await this.client.get('/sdk/admin/users');
172
- return res.data;
173
- }
174
-
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;
178
- }
179
-
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;
183
- }
184
-
185
- async updateRBAC(userId, permissions) {
186
- const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
187
- return res.data;
168
+ this.client = axios.create({ baseURL: this.baseUrl, headers: { 'Authorization': `Bearer ${this.apiKey}` } });
188
169
  }
170
+ async getUsers() { const res = await this.client.get('/sdk/admin/users'); return res.data; }
171
+ async createUser(data) { const res = await this.client.post('/sdk/admin/users', data); return res.data; }
172
+ async updateUser(userId, data) { const res = await this.client.put(`/sdk/admin/users/${userId}`, data); return res.data; }
173
+ async updateRBAC(userId, permissions) { const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions }); return res.data; }
174
+ async deleteUser(userId) { const res = await this.client.delete(`/sdk/admin/users/${userId}`); return res.data; }
175
+ async getRoles() { const res = await this.client.get('/sdk/admin/roles'); return res.data; }
176
+ async createRole(data) { const res = await this.client.post('/sdk/admin/roles', data); return res.data; }
177
+ }
189
178
 
190
- async deleteUser(userId) {
191
- const res = await this.client.delete(`/sdk/admin/users/${userId}`);
192
- return res.data;
179
+ class AipIAMClient {
180
+ constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
181
+ if (!apiKey) throw new Error("AIP IAM Client requires an API Key");
182
+ this.apiKey = apiKey;
183
+ this.baseUrl = getSafeMasterUrl(masterUrl);
184
+ this.client = axios.create({ baseURL: this.baseUrl });
193
185
  }
186
+ async loginUser(req, res, credentials) {
187
+ let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
188
+ const forwardedFor = req.headers['x-forwarded-for'];
189
+ if (forwardedFor) clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
194
190
 
195
- // --- Role Management ---
196
- async getRoles() {
197
- const res = await this.client.get('/sdk/admin/roles');
198
- return res.data;
191
+ const authRes = await this.client.post('/iam/auth', { api_key: this.apiKey, ip: clientIp, ...credentials });
192
+
193
+ if (authRes.data.session_token) {
194
+ const isProd = process.env.NODE_ENV === 'production';
195
+ if (typeof res.cookie === 'function') {
196
+ res.cookie('aip_session', authRes.data.session_token, { httpOnly: true, secure: isProd, sameSite: isProd ? 'none' : 'lax', path: '/', maxAge: 12 * 60 * 60 * 1000 });
197
+ } else {
198
+ res.setHeader('Set-Cookie', `aip_session=${authRes.data.session_token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`);
199
+ }
200
+ }
201
+ return authRes.data;
199
202
  }
200
-
201
- async createRole({ name, slug, permissions }) {
202
- const res = await this.client.post('/sdk/admin/roles', { name, slug, permissions });
203
- return res.data;
203
+ logoutUser(res) {
204
+ const isProd = process.env.NODE_ENV === 'production';
205
+ if (typeof res.clearCookie === 'function') {
206
+ res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
207
+ } else {
208
+ res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
209
+ }
204
210
  }
205
211
  }
206
212
 
207
- // 🛡️ Attach the Admin Client directly to the Guard export for backward compatibility
208
213
  aipGuard.AipAdminClient = AipAdminClient;
209
-
214
+ aipGuard.AipIAMClient = AipIAMClient;
210
215
  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.8",
4
4
  "description": "Enterprise-grade WAF and IAM security middleware for Node.js.",
5
5
  "main": "index.js",
6
6
  "scripts": {