aip-master-node-sumit 1.0.6 → 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 +116 -85
  2. package/package.json +2 -2
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
  }
@@ -32,23 +45,43 @@ if (API_KEY) {
32
45
  const aipGuard = (options = { requireLogin: true }) => {
33
46
  return async (req, res, next) => {
34
47
 
35
- // LAYER 1: INFRASTRUCTURE SHIELD
48
+ // ==========================================
49
+ // 🛡️ LAYER 0: DYNAMIC CORS & SECURITY HEADERS
50
+ // ==========================================
51
+ const origin = req.headers.origin || req.headers.Origin;
52
+
53
+ if (origin) {
54
+ res.setHeader('Access-Control-Allow-Origin', origin);
55
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
56
+ } else {
57
+ res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
58
+ }
59
+
60
+ res.setHeader('X-Frame-Options', 'DENY');
61
+ res.setHeader('X-Content-Type-Options', 'nosniff');
62
+ res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
63
+
64
+ if (req.method === 'OPTIONS') {
65
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
66
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-AIP-Token, X-Requested-With, Accept');
67
+ return res.status(200).end();
68
+ }
69
+
70
+ // ==========================================
71
+ // 🛡️ LAYER 1: INFRASTRUCTURE SHIELD
72
+ // ==========================================
36
73
  const MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
37
74
 
38
75
  if (req.headers['transfer-encoding'] && req.headers['transfer-encoding'].includes('chunked')) {
39
- return res.status(411).send("AIP Infrastructure Shield: Chunked encoding rejected.");
76
+ return res.status(411).json({ error: "AIP Infrastructure Shield: Chunked encoding rejected." });
40
77
  }
41
-
42
78
  if (req.headers['content-length'] && parseInt(req.headers['content-length']) > MAX_PAYLOAD_BYTES) {
43
- return res.status(413).send("AIP Infrastructure Shield: Payload Too Large.");
79
+ return res.status(413).json({ error: "AIP Infrastructure Shield: Payload Too Large." });
44
80
  }
45
-
46
- res.setHeader('X-Frame-Options', 'DENY');
47
- res.setHeader('X-Content-Type-Options', 'nosniff');
48
- res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
49
- res.setHeader('Access-Control-Allow-Origin', process.env.AIP_ALLOWED_ORIGIN || '*');
50
81
 
51
- 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;
52
85
 
53
86
  if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
54
87
  token = req.headers.authorization.split(' ')[1];
@@ -56,13 +89,14 @@ const aipGuard = (options = { requireLogin: true }) => {
56
89
  token = req.headers['x-aip-token'];
57
90
  }
58
91
 
59
- // LAYER 2: IDENTITY CHECK & RBAC
92
+ // ==========================================
93
+ // 🛡️ LAYER 2: IDENTITY CHECK & RBAC
94
+ // ==========================================
60
95
  if (options.requireLogin) {
61
- if (!token) return res.status(401).send("Unauthorized: Missing AIP Session Token");
96
+ if (!token) return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
62
97
 
63
98
  try {
64
99
  const currentAction = req.method + " " + (req.baseUrl + req.path);
65
-
66
100
  const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
67
101
  { session_token: token, action: currentAction },
68
102
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
@@ -70,115 +104,112 @@ const aipGuard = (options = { requireLogin: true }) => {
70
104
 
71
105
  req.user = authRes.data.user;
72
106
 
107
+ // 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
73
108
  const isProd = process.env.NODE_ENV === 'production';
74
- 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
+ }
75
117
 
76
118
  } catch (error) {
77
119
  const status = error.response?.status || 500;
78
120
  const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
79
121
 
80
- if (status === 401 && res.clearCookie) {
81
- 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
+ }
82
129
  }
83
130
 
84
- return res.status(status === 401 ? 401 : 403).send(errMsg);
131
+ return res.status(status === 401 ? 401 : 403).json({ error: errMsg });
85
132
  }
86
133
  }
87
134
 
88
- // LAYER 3: WAF THREAT SCAN
135
+ // ==========================================
136
+ // 🛡️ LAYER 3: WAF THREAT SCAN
137
+ // ==========================================
89
138
  try {
90
- const payloadData = JSON.stringify({
91
- body: req.body || {},
92
- query: req.query || {}
93
- });
94
-
139
+ const payloadData = JSON.stringify({ body: req.body || {}, query: req.query || {} });
95
140
  const forwardedFor = req.headers['x-forwarded-for'];
96
141
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
97
-
98
142
  if (forwardedFor) {
99
- clientIp = Array.isArray(forwardedFor)
100
- ? forwardedFor[0].trim()
101
- : forwardedFor.split(',')[0].trim();
143
+ clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
102
144
  }
103
145
 
104
146
  const wafRes = await axios.post(`${AIP_MASTER_API}/verify`, {
105
- api_key: API_KEY,
106
- ip: clientIp,
107
- method: req.method,
108
- url: req.originalUrl,
109
- payload: payloadData,
110
- origin: req.headers.origin || "",
111
- 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'] || "",
112
149
  session_token: token || ""
113
150
  });
114
151
 
115
152
  if (wafRes.data.action === "block") {
116
- return res.status(403).send(`AIP Firewall Blocked Request: ${wafRes.data.reason}`);
153
+ return res.status(403).json({ error: `AIP Firewall Blocked Request: ${wafRes.data.reason}` });
117
154
  }
118
155
 
119
156
  } catch (error) {
120
- console.error("WAF Engine Unreachable");
121
- return res.status(500).send("Security verification failed.");
157
+ return res.status(500).json({ error: "Security verification failed." });
122
158
  }
123
-
124
159
  next();
125
160
  };
126
161
  };
127
162
 
128
- // ==========================================
129
- // 🛡️ NEW: IaaS Admin Client SDK
130
- // ==========================================
131
163
  class AipAdminClient {
132
164
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
133
165
  if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
134
166
  this.apiKey = apiKey;
135
167
  this.baseUrl = getSafeMasterUrl(masterUrl);
136
-
137
- this.client = axios.create({
138
- baseURL: this.baseUrl,
139
- headers: { 'Authorization': `Bearer ${this.apiKey}` }
140
- });
141
- }
142
-
143
- // --- User Management ---
144
- async getUsers() {
145
- const res = await this.client.get('/sdk/admin/users');
146
- return res.data;
147
- }
148
-
149
- async createUser({ name, email, password, role_id, send_email = false }) {
150
- const res = await this.client.post('/sdk/admin/users', { name, email, password, role_id, send_email });
151
- return res.data;
152
- }
153
-
154
- async updateUser(userId, { name, role_id }) {
155
- const res = await this.client.put(`/sdk/admin/users/${userId}`, { name, role_id });
156
- return res.data;
157
- }
158
-
159
- async updateRBAC(userId, permissions) {
160
- const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
161
- return res.data;
168
+ this.client = axios.create({ baseURL: this.baseUrl, headers: { 'Authorization': `Bearer ${this.apiKey}` } });
162
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
+ }
163
178
 
164
- async deleteUser(userId) {
165
- const res = await this.client.delete(`/sdk/admin/users/${userId}`);
166
- 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 });
167
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();
168
190
 
169
- // --- Role Management ---
170
- async getRoles() {
171
- const res = await this.client.get('/sdk/admin/roles');
172
- 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;
173
202
  }
174
-
175
- async createRole({ name, slug, permissions }) {
176
- const res = await this.client.post('/sdk/admin/roles', { name, slug, permissions });
177
- 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
+ }
178
210
  }
179
211
  }
180
212
 
181
- // 🛡️ Attach the Admin Client directly to the Guard export for backward compatibility
182
213
  aipGuard.AipAdminClient = AipAdminClient;
183
-
214
+ aipGuard.AipIAMClient = AipIAMClient;
184
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.6",
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": {
@@ -17,4 +17,4 @@
17
17
  "dependencies": {
18
18
  "axios": "^1.13.6"
19
19
  }
20
- }
20
+ }