snow-flow 1.3.25 â 1.3.28
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/.claude/config.json +33 -9
- package/.claude-flow/queen/queen-memory.db +0 -0
- package/.roo/README.md +56 -0
- package/.roo/workflows/basic-tdd.json +32 -0
- package/.roomodes +122 -0
- package/CLAUDE.md +140 -752
- package/claude-flow +30 -75
- package/dist/cli.js +250 -7
- package/dist/compliance/advanced-compliance-system.js +857 -0
- package/dist/compliance/index.js +8 -0
- package/dist/documentation/index.js +8 -0
- package/dist/documentation/self-documenting-system.js +1006 -0
- package/dist/healing/index.js +8 -0
- package/dist/healing/self-healing-system.js +1041 -0
- package/dist/intelligence/performance-recommendations-engine.js +479 -4
- package/dist/managers/scope-manager.js +5 -2
- package/dist/mcp/servicenow-deployment-mcp.js +113 -15
- package/dist/mcp/servicenow-flow-composer-mcp.js +4 -2
- package/dist/mcp/servicenow-intelligent-mcp.js +351 -0
- package/dist/mcp/servicenow-operations-mcp.js +2 -2
- package/dist/memory/memory-system.js +146 -0
- package/dist/monitoring/enhanced-monitoring-system.js +1085 -0
- package/dist/optimization/cost-optimization-engine.js +771 -0
- package/dist/optimization/flow-performance-optimizer.js +723 -0
- package/dist/optimization/index.js +10 -0
- package/dist/orchestration/flow-update-orchestrator.js +648 -0
- package/dist/rollback/smart-rollback-system.js +462 -0
- package/dist/templates/flow-template-system.js +625 -0
- package/dist/testing/flow-testing-automation.js +641 -0
- package/dist/testing/integration-test-suite.js +890 -0
- package/dist/utils/flow-structure-builder.js +1 -1
- package/dist/utils/servicenow-client.js +50 -2
- package/dist/utils/snow-oauth.js +66 -8
- package/dist/utils/xml-first-flow-generator.js +6 -0
- package/dist/version.js +13 -1
- package/package.json +1 -1
|
@@ -64,7 +64,7 @@ function generateFlowComponents(flowDefinition, flowSysId) {
|
|
|
64
64
|
type: 'flow',
|
|
65
65
|
status: 'published',
|
|
66
66
|
active: true,
|
|
67
|
-
run_as: '
|
|
67
|
+
run_as: 'user', // đ SEC-001 FIX: Default to 'user' to prevent privilege escalation
|
|
68
68
|
sys_class_name: 'sys_hub_flow',
|
|
69
69
|
table: flowDefinition.table,
|
|
70
70
|
category: 'custom',
|
|
@@ -10,6 +10,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.ServiceNowClient = void 0;
|
|
12
12
|
const axios_1 = __importDefault(require("axios"));
|
|
13
|
+
const https_1 = __importDefault(require("https"));
|
|
13
14
|
const snow_oauth_1 = require("./snow-oauth");
|
|
14
15
|
const action_type_cache_1 = require("./action-type-cache");
|
|
15
16
|
const snow_flow_config_js_1 = require("../config/snow-flow-config.js");
|
|
@@ -25,12 +26,59 @@ class ServiceNowClient {
|
|
|
25
26
|
this.lastTokenRefresh = 0;
|
|
26
27
|
this.logger = new logger_1.Logger('ServiceNowClient');
|
|
27
28
|
this.oauth = new snow_oauth_1.ServiceNowOAuth();
|
|
29
|
+
// đ SSL/TLS Certificate Validation Fix: Add proper HTTPS agent with certificate validation
|
|
30
|
+
const httpsAgent = new https_1.default.Agent({
|
|
31
|
+
rejectUnauthorized: true, // Enforce certificate validation
|
|
32
|
+
checkServerIdentity: (hostname, cert) => {
|
|
33
|
+
// Custom certificate validation for ServiceNow instances
|
|
34
|
+
// Allow *.service-now.com and user-configured instances
|
|
35
|
+
const validHosts = [
|
|
36
|
+
/.*\.service-now\.com$/,
|
|
37
|
+
/.*\.servicenow\.com$/
|
|
38
|
+
];
|
|
39
|
+
// Get configured instance hostname
|
|
40
|
+
const instanceUrl = process.env.SNOW_INSTANCE;
|
|
41
|
+
if (instanceUrl) {
|
|
42
|
+
try {
|
|
43
|
+
const instanceHostname = new URL(instanceUrl.startsWith('http') ? instanceUrl : `https://${instanceUrl}`).hostname;
|
|
44
|
+
validHosts.push(new RegExp(`^${instanceHostname.replace(/\./g, '\\.')}$`));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Invalid URL format, rely on default validation
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Check if hostname matches allowed patterns
|
|
51
|
+
if (validHosts.some(pattern => pattern.test(hostname))) {
|
|
52
|
+
return undefined; // Valid hostname
|
|
53
|
+
}
|
|
54
|
+
// For development/testing environments, allow localhost/127.0.0.1
|
|
55
|
+
if (process.env.NODE_ENV === 'development' &&
|
|
56
|
+
(hostname === 'localhost' || hostname === '127.0.0.1' || hostname.startsWith('192.168.'))) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return new Error(`Certificate hostname mismatch: ${hostname} not allowed`);
|
|
60
|
+
},
|
|
61
|
+
// secureProtocol removed to avoid conflict with minVersion/maxVersion
|
|
62
|
+
ciphers: [
|
|
63
|
+
'ECDHE-RSA-AES128-GCM-SHA256',
|
|
64
|
+
'ECDHE-RSA-AES256-GCM-SHA384',
|
|
65
|
+
'ECDHE-RSA-AES128-SHA256',
|
|
66
|
+
'ECDHE-RSA-AES256-SHA384'
|
|
67
|
+
].join(':'), // Only allow secure ciphers
|
|
68
|
+
honorCipherOrder: true,
|
|
69
|
+
maxVersion: 'TLSv1.3',
|
|
70
|
+
minVersion: 'TLSv1.2'
|
|
71
|
+
});
|
|
28
72
|
this.client = axios_1.default.create({
|
|
29
73
|
timeout: snow_flow_config_js_1.snowFlowConfig.servicenow.timeout,
|
|
30
74
|
headers: {
|
|
31
75
|
'Content-Type': 'application/json',
|
|
32
76
|
'Accept': 'application/json'
|
|
33
|
-
}
|
|
77
|
+
},
|
|
78
|
+
httpsAgent, // Use secure HTTPS agent with certificate validation
|
|
79
|
+
// Additional security headers
|
|
80
|
+
maxRedirects: 5,
|
|
81
|
+
validateStatus: (status) => status < 400 // Reject 4xx and 5xx by default
|
|
34
82
|
});
|
|
35
83
|
// đ§ CRITICAL FIX: Add makeRequest method to Axios instance to fix phantom calls
|
|
36
84
|
// Some code expects makeRequest to exist on this.client (the Axios instance)
|
|
@@ -1328,7 +1376,7 @@ snow_create_flow({
|
|
|
1328
1376
|
active: true, // Always create as active
|
|
1329
1377
|
internal_name: this.sanitizeInternalName(flow.name),
|
|
1330
1378
|
category: flow.category || 'custom',
|
|
1331
|
-
run_as: flow.run_as || '
|
|
1379
|
+
run_as: flow.run_as || 'user', // đ SEC-001 FIX: Default to 'user' to prevent privilege escalation
|
|
1332
1380
|
status: 'published', // đ§ FIX: Ensure flow is published, not draft
|
|
1333
1381
|
validated: true, // đ§ FIX: Mark as validated to allow activation
|
|
1334
1382
|
// Include complete flow definition to prevent corruption
|
package/dist/utils/snow-oauth.js
CHANGED
|
@@ -21,10 +21,33 @@ const snow_flow_config_js_1 = require("../config/snow-flow-config.js");
|
|
|
21
21
|
const unified_auth_store_js_1 = require("./unified-auth-store.js");
|
|
22
22
|
class ServiceNowOAuth {
|
|
23
23
|
constructor() {
|
|
24
|
+
// đ SEC-002 FIX: Add rate limiting to prevent authentication bypass attacks
|
|
25
|
+
this.lastTokenRequest = 0;
|
|
26
|
+
this.tokenRequestCount = 0;
|
|
27
|
+
this.TOKEN_REQUEST_WINDOW_MS = 60000; // 1 minute window
|
|
28
|
+
this.MAX_TOKEN_REQUESTS_PER_WINDOW = 10; // Max 10 token requests per minute
|
|
24
29
|
// Store tokens in user's home directory
|
|
25
30
|
const configDir = process.env.SNOW_FLOW_HOME || (0, path_1.join)(os_1.default.homedir(), '.snow-flow');
|
|
26
31
|
this.tokenPath = (0, path_1.join)(configDir, 'auth.json');
|
|
27
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* đ SEC-002 FIX: Check rate limiting for token requests to prevent brute force attacks
|
|
35
|
+
*/
|
|
36
|
+
checkTokenRequestRateLimit() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
// Reset counter if window has passed
|
|
39
|
+
if (now - this.lastTokenRequest > this.TOKEN_REQUEST_WINDOW_MS) {
|
|
40
|
+
this.tokenRequestCount = 0;
|
|
41
|
+
this.lastTokenRequest = now;
|
|
42
|
+
}
|
|
43
|
+
// Check if within rate limit
|
|
44
|
+
if (this.tokenRequestCount >= this.MAX_TOKEN_REQUESTS_PER_WINDOW) {
|
|
45
|
+
console.warn('đ Rate limit exceeded: Too many token requests. Please wait before retrying.');
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
this.tokenRequestCount++;
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
28
51
|
/**
|
|
29
52
|
* Generate a random state parameter for CSRF protection
|
|
30
53
|
*/
|
|
@@ -62,8 +85,27 @@ class ServiceNowOAuth {
|
|
|
62
85
|
/**
|
|
63
86
|
* Initialize OAuth flow - opens browser and handles callback
|
|
64
87
|
*/
|
|
88
|
+
/**
|
|
89
|
+
* đ§ CRIT-002 FIX: Normalize instance URL to prevent trailing slash 400 errors
|
|
90
|
+
*/
|
|
91
|
+
normalizeInstanceUrl(instance) {
|
|
92
|
+
// Remove any trailing slashes that cause 400 errors
|
|
93
|
+
let normalized = instance.replace(/\/+$/, '');
|
|
94
|
+
// Add https:// if missing
|
|
95
|
+
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
|
|
96
|
+
normalized = `https://${normalized}`;
|
|
97
|
+
}
|
|
98
|
+
// Ensure .service-now.com suffix if it's just the instance name
|
|
99
|
+
if (!normalized.includes('.service-now.com') && !normalized.includes('localhost') && !normalized.includes('127.0.0.1')) {
|
|
100
|
+
const instanceName = normalized.replace('https://', '').replace('http://', '');
|
|
101
|
+
normalized = `https://${instanceName}.service-now.com`;
|
|
102
|
+
}
|
|
103
|
+
return normalized;
|
|
104
|
+
}
|
|
65
105
|
async authenticate(instance, clientId, clientSecret) {
|
|
66
106
|
try {
|
|
107
|
+
// đ§ CRIT-002 FIX: Apply URL normalization
|
|
108
|
+
const normalizedInstance = this.normalizeInstanceUrl(instance);
|
|
67
109
|
// Validate client secret format
|
|
68
110
|
const secretValidation = this.validateClientSecret(clientSecret);
|
|
69
111
|
if (!secretValidation.valid) {
|
|
@@ -94,15 +136,15 @@ class ServiceNowOAuth {
|
|
|
94
136
|
error: `Port ${port} is already in use. Please free up the port and try again.`
|
|
95
137
|
};
|
|
96
138
|
}
|
|
97
|
-
// Store credentials temporarily
|
|
139
|
+
// Store credentials temporarily with normalized instance
|
|
98
140
|
this.credentials = {
|
|
99
|
-
instance:
|
|
141
|
+
instance: normalizedInstance.replace('https://', '').replace('http://', ''),
|
|
100
142
|
clientId,
|
|
101
143
|
clientSecret,
|
|
102
144
|
redirectUri
|
|
103
145
|
};
|
|
104
146
|
console.log('đ Starting ServiceNow OAuth flow...');
|
|
105
|
-
console.log(`đ Instance: ${
|
|
147
|
+
console.log(`đ Instance: ${normalizedInstance}`);
|
|
106
148
|
console.log(`đ Client ID: ${clientId}`);
|
|
107
149
|
console.log(`đ Redirect URI: ${redirectUri}`);
|
|
108
150
|
// Generate state parameter for CSRF protection
|
|
@@ -110,18 +152,18 @@ class ServiceNowOAuth {
|
|
|
110
152
|
// Generate PKCE parameters
|
|
111
153
|
this.generatePKCE();
|
|
112
154
|
// Generate authorization URL
|
|
113
|
-
const authUrl = this.generateAuthUrl(instance, clientId, redirectUri);
|
|
155
|
+
const authUrl = this.generateAuthUrl(this.credentials.instance, clientId, redirectUri);
|
|
114
156
|
console.log('\nđ Authorization URL generated:');
|
|
115
157
|
console.log(`${authUrl}\n`);
|
|
116
158
|
// Start local server to handle callback
|
|
117
159
|
const authResult = await this.startCallbackServer(redirectUri, port);
|
|
118
160
|
if (authResult.success && authResult.accessToken) {
|
|
119
|
-
// Save tokens
|
|
161
|
+
// Save tokens with normalized instance
|
|
120
162
|
await this.saveTokens({
|
|
121
163
|
accessToken: authResult.accessToken,
|
|
122
164
|
refreshToken: authResult.refreshToken || '',
|
|
123
165
|
expiresIn: authResult.expiresIn || 3600,
|
|
124
|
-
instance: instance
|
|
166
|
+
instance: this.credentials.instance,
|
|
125
167
|
clientId,
|
|
126
168
|
clientSecret
|
|
127
169
|
});
|
|
@@ -303,6 +345,13 @@ class ServiceNowOAuth {
|
|
|
303
345
|
*/
|
|
304
346
|
async exchangeCodeForTokens(code) {
|
|
305
347
|
try {
|
|
348
|
+
// đ SEC-002 FIX: Apply rate limiting to prevent authentication bypass attacks
|
|
349
|
+
if (!this.checkTokenRequestRateLimit()) {
|
|
350
|
+
return {
|
|
351
|
+
success: false,
|
|
352
|
+
error: 'Rate limit exceeded. Too many token requests. Please wait 1 minute before retrying.'
|
|
353
|
+
};
|
|
354
|
+
}
|
|
306
355
|
const tokenUrl = `https://${this.credentials.instance}/oauth_token.do`;
|
|
307
356
|
const response = await axios_1.default.post(tokenUrl, new URLSearchParams({
|
|
308
357
|
grant_type: 'authorization_code',
|
|
@@ -314,7 +363,8 @@ class ServiceNowOAuth {
|
|
|
314
363
|
}), {
|
|
315
364
|
headers: {
|
|
316
365
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
317
|
-
}
|
|
366
|
+
},
|
|
367
|
+
timeout: 15000 // đ SEC-002 FIX: 15 second timeout to prevent hanging requests
|
|
318
368
|
});
|
|
319
369
|
const data = response.data;
|
|
320
370
|
if (data.access_token) {
|
|
@@ -434,6 +484,13 @@ class ServiceNowOAuth {
|
|
|
434
484
|
*/
|
|
435
485
|
async refreshAccessToken(tokens) {
|
|
436
486
|
try {
|
|
487
|
+
// đ SEC-002 FIX: Apply rate limiting to prevent authentication bypass attacks
|
|
488
|
+
if (!this.checkTokenRequestRateLimit()) {
|
|
489
|
+
return {
|
|
490
|
+
success: false,
|
|
491
|
+
error: 'Rate limit exceeded. Too many token requests. Please wait 1 minute before retrying.'
|
|
492
|
+
};
|
|
493
|
+
}
|
|
437
494
|
// If no tokens provided, load from file
|
|
438
495
|
if (!tokens) {
|
|
439
496
|
tokens = await this.loadTokens();
|
|
@@ -453,7 +510,8 @@ class ServiceNowOAuth {
|
|
|
453
510
|
}), {
|
|
454
511
|
headers: {
|
|
455
512
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
456
|
-
}
|
|
513
|
+
},
|
|
514
|
+
timeout: 15000 // đ SEC-002 FIX: 15 second timeout to prevent hanging requests
|
|
457
515
|
});
|
|
458
516
|
const data = response.data;
|
|
459
517
|
if (data.access_token) {
|
|
@@ -453,6 +453,12 @@ class XMLFirstFlowGenerator {
|
|
|
453
453
|
getActionTypeId(actionType) {
|
|
454
454
|
return exports.ACTION_TYPE_SYS_IDS[actionType] || exports.ACTION_TYPE_SYS_IDS['script'];
|
|
455
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Generate complete Flow XML - alias for backward compatibility
|
|
458
|
+
*/
|
|
459
|
+
generateCompleteFlowXML(flowDef) {
|
|
460
|
+
return this.generateFlowUpdateSetXML(flowDef);
|
|
461
|
+
}
|
|
456
462
|
/**
|
|
457
463
|
* Save XML to file
|
|
458
464
|
*/
|
package/dist/version.js
CHANGED
|
@@ -7,12 +7,24 @@ exports.VERSION_INFO = exports.VERSION = void 0;
|
|
|
7
7
|
exports.getVersionString = getVersionString;
|
|
8
8
|
exports.getLatestFeatures = getLatestFeatures;
|
|
9
9
|
exports.isLatestVersion = isLatestVersion;
|
|
10
|
-
exports.VERSION = '1.3.
|
|
10
|
+
exports.VERSION = '1.3.27';
|
|
11
11
|
exports.VERSION_INFO = {
|
|
12
12
|
version: exports.VERSION,
|
|
13
13
|
name: 'Snow-Flow',
|
|
14
14
|
description: 'ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development',
|
|
15
15
|
features: {
|
|
16
|
+
'1.3.27': [
|
|
17
|
+
'đ¤ AUTONOMOUS SYSTEMS: All 4 autonomous systems now ENABLED BY DEFAULT in swarm command!',
|
|
18
|
+
'â
SELF-DOCUMENTING: Automatic documentation generation and updates (default: enabled)',
|
|
19
|
+
'đ° COST OPTIMIZATION: AI-driven cost management with auto-optimization (default: enabled)',
|
|
20
|
+
'đ COMPLIANCE MONITORING: Multi-framework compliance with auto-remediation (default: enabled)',
|
|
21
|
+
'đĨ SELF-HEALING: Predictive failure detection with automatic recovery (default: enabled)',
|
|
22
|
+
'âī¸ FLEXIBLE CONTROL: Use --no-autonomous-* flags to disable specific systems',
|
|
23
|
+
'đĢ GLOBAL DISABLE: Use --no-autonomous-all to disable all autonomous systems',
|
|
24
|
+
'đĒ FORCE OVERRIDE: Use --autonomous-all to force enable all (overrides --no- flags)',
|
|
25
|
+
'đ¯ PERFECT ORCHESTRATOR: True "ÊÊn command voor alles" - zero manual intervention needed!',
|
|
26
|
+
'Based on user request: "oke maar kunnen we het niet als standaard aanzetten dat al deze dingen beschikbaar zijn?"'
|
|
27
|
+
],
|
|
16
28
|
'1.3.16': [
|
|
17
29
|
'đ VERSION BUMP: Clean version update for npm publish',
|
|
18
30
|
'⨠INCLUDES: All improvements from v1.3.15',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.28",
|
|
4
4
|
"description": "ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development inspired by claude-flow. Transform complex workflows into elegant one-command orchestration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|