@odavl/guardian 2.0.0 → 2.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/CHANGELOG.md +210 -210
- package/LICENSE +21 -21
- package/README.md +297 -184
- package/bin/guardian.js +2242 -2221
- package/config/README.md +59 -59
- package/config/guardian.config.json +54 -54
- package/config/guardian.policy.json +12 -12
- package/config/profiles/docs.yaml +18 -18
- package/config/profiles/ecommerce.yaml +17 -17
- package/config/profiles/landing-demo.yaml +16 -16
- package/config/profiles/marketing.yaml +18 -18
- package/config/profiles/saas.yaml +21 -21
- package/flows/example-login-flow.json +36 -36
- package/flows/example-signup-flow.json +44 -44
- package/package.json +124 -116
- package/policies/enterprise.json +12 -12
- package/policies/landing-demo.json +22 -22
- package/policies/saas.json +12 -12
- package/policies/startup.json +12 -12
- package/src/enterprise/audit-logger.js +166 -166
- package/src/enterprise/pdf-exporter.js +267 -267
- package/src/enterprise/rbac-gate.js +142 -142
- package/src/enterprise/rbac.js +239 -239
- package/src/enterprise/site-manager.js +180 -180
- package/src/founder/feedback-system.js +156 -156
- package/src/founder/founder-tracker.js +213 -213
- package/src/founder/usage-signals.js +141 -141
- package/src/guardian/action-hints.js +439 -439
- package/src/guardian/alert-ledger.js +121 -121
- package/src/guardian/artifact-sanitizer.js +56 -56
- package/src/guardian/attempt-engine.js +1069 -1029
- package/src/guardian/attempt-registry.js +267 -267
- package/src/guardian/attempt-relevance.js +106 -106
- package/src/guardian/attempt-reporter.js +513 -507
- package/src/guardian/attempt.js +274 -273
- package/src/guardian/attempts-filter.js +63 -63
- package/src/guardian/auto-attempt-builder.js +283 -283
- package/src/guardian/baseline-registry.js +177 -177
- package/src/guardian/baseline-reporter.js +143 -143
- package/src/guardian/baseline-storage.js +285 -285
- package/src/guardian/baseline.js +535 -534
- package/src/guardian/behavioral-signals.js +261 -261
- package/src/guardian/breakage-intelligence.js +224 -224
- package/src/guardian/browser-pool.js +131 -131
- package/src/guardian/browser.js +119 -119
- package/src/guardian/canonical-truth.js +308 -308
- package/src/guardian/ci-cli.js +121 -121
- package/src/guardian/ci-gate.js +96 -96
- package/src/guardian/ci-mode.js +15 -15
- package/src/guardian/ci-output.js +55 -38
- package/src/guardian/cli-summary.js +102 -102
- package/src/guardian/confidence-signals.js +251 -251
- package/src/guardian/config-loader.js +161 -161
- package/src/guardian/config-validator.js +285 -283
- package/src/guardian/coverage-model.js +239 -239
- package/src/guardian/coverage-packs.js +58 -58
- package/src/guardian/crawler.js +142 -142
- package/src/guardian/data-guardian-detector.js +189 -189
- package/src/guardian/decision-authority.js +746 -725
- package/src/guardian/detection-layers.js +271 -271
- package/src/guardian/determinism.js +146 -146
- package/src/guardian/discovery-engine.js +661 -661
- package/src/guardian/drift-detector.js +100 -100
- package/src/guardian/enhanced-html-reporter.js +522 -522
- package/src/guardian/env-guard.js +128 -127
- package/src/guardian/error-clarity.js +399 -399
- package/src/guardian/export-contract.js +196 -196
- package/src/guardian/fail-safe.js +212 -212
- package/src/guardian/failure-intelligence.js +173 -173
- package/src/guardian/failure-taxonomy.js +169 -169
- package/src/guardian/final-outcome.js +206 -206
- package/src/guardian/first-run-profile.js +89 -89
- package/src/guardian/first-run.js +65 -67
- package/src/guardian/flag-validator.js +111 -111
- package/src/guardian/flow-executor.js +641 -639
- package/src/guardian/flow-registry.js +67 -67
- package/src/guardian/honesty.js +394 -394
- package/src/guardian/html-reporter.js +416 -416
- package/src/guardian/human-intent-resolver.js +296 -296
- package/src/guardian/human-interaction-model.js +351 -351
- package/src/guardian/human-journey-context.js +184 -184
- package/src/guardian/human-navigator.js +544 -544
- package/src/guardian/human-reporter.js +435 -431
- package/src/guardian/index.js +226 -221
- package/src/guardian/init-command.js +143 -143
- package/src/guardian/intent-detector.js +148 -146
- package/src/guardian/journey-definitions.js +132 -132
- package/src/guardian/journey-scan-cli.js +142 -145
- package/src/guardian/journey-scanner.js +583 -583
- package/src/guardian/junit-reporter.js +281 -281
- package/src/guardian/language-detection.js +99 -99
- package/src/guardian/live-alert.js +56 -56
- package/src/guardian/live-baseline-compare.js +146 -146
- package/src/guardian/live-cli.js +95 -95
- package/src/guardian/live-guardian.js +210 -210
- package/src/guardian/live-scheduler-runner.js +137 -137
- package/src/guardian/live-scheduler-state.js +167 -168
- package/src/guardian/live-scheduler.js +146 -146
- package/src/guardian/live-state.js +110 -110
- package/src/guardian/market-criticality.js +335 -335
- package/src/guardian/market-reporter.js +577 -577
- package/src/guardian/network-trace.js +178 -178
- package/src/guardian/obs-logger.js +110 -110
- package/src/guardian/observed-capabilities.js +427 -427
- package/src/guardian/output-contract.js +154 -0
- package/src/guardian/output-readability.js +264 -264
- package/src/guardian/parallel-executor.js +116 -116
- package/src/guardian/path-safety.js +56 -56
- package/src/guardian/pattern-analyzer.js +348 -348
- package/src/guardian/policy.js +432 -434
- package/src/guardian/prelaunch-gate.js +193 -193
- package/src/guardian/prerequisite-checker.js +101 -101
- package/src/guardian/preset-loader.js +152 -157
- package/src/guardian/profile-loader.js +96 -96
- package/src/guardian/reality.js +3025 -2826
- package/src/guardian/realworld-scenarios.js +94 -94
- package/src/guardian/reporter.js +167 -167
- package/src/guardian/retry-policy.js +123 -123
- package/src/guardian/root-cause-analysis.js +171 -171
- package/src/guardian/rules-engine.js +558 -558
- package/src/guardian/run-artifacts.js +212 -212
- package/src/guardian/run-cleanup.js +207 -207
- package/src/guardian/run-export.js +522 -522
- package/src/guardian/run-latest.js +90 -90
- package/src/guardian/run-list.js +211 -211
- package/src/guardian/run-summary.js +20 -20
- package/src/guardian/runtime-root.js +246 -246
- package/src/guardian/safety.js +248 -248
- package/src/guardian/scan-presets.js +133 -149
- package/src/guardian/screenshot.js +152 -152
- package/src/guardian/secret-hygiene.js +44 -44
- package/src/guardian/selector-fallbacks.js +394 -394
- package/src/guardian/semantic-contact-detection.js +255 -255
- package/src/guardian/semantic-contact-finder.js +201 -201
- package/src/guardian/semantic-targets.js +234 -234
- package/src/guardian/site-intelligence.js +588 -588
- package/src/guardian/site-introspection.js +257 -257
- package/src/guardian/sitemap.js +225 -225
- package/src/guardian/smoke.js +283 -258
- package/src/guardian/snapshot-schema.js +177 -290
- package/src/guardian/snapshot.js +430 -397
- package/src/guardian/stability-scorer.js +169 -169
- package/src/guardian/success-evaluator.js +214 -214
- package/src/guardian/template-command.js +184 -184
- package/src/guardian/text-formatters.js +426 -426
- package/src/guardian/timeout-profiles.js +57 -57
- package/src/guardian/truth/attempt.contract.js +158 -0
- package/src/guardian/truth/decision.contract.js +275 -0
- package/src/guardian/truth/snapshot.contract.js +363 -0
- package/src/guardian/validators.js +323 -323
- package/src/guardian/verdict-card.js +474 -474
- package/src/guardian/verdict-clarity.js +298 -298
- package/src/guardian/verdict-policy.js +363 -363
- package/src/guardian/verdict.js +333 -333
- package/src/guardian/verdicts.js +79 -74
- package/src/guardian/visual-diff.js +247 -247
- package/src/guardian/wait-for-outcome.js +119 -119
- package/src/guardian/watch-runner.js +181 -181
- package/src/guardian/watchdog-diff.js +167 -167
- package/src/guardian/webhook.js +206 -206
- package/src/payments/stripe-checkout.js +169 -169
- package/src/plans/plan-definitions.js +148 -148
- package/src/plans/plan-manager.js +211 -211
- package/src/plans/usage-tracker.js +210 -210
- package/src/recipes/recipe-engine.js +188 -188
- package/src/recipes/recipe-failure-analysis.js +159 -159
- package/src/recipes/recipe-registry.js +134 -134
- package/src/recipes/recipe-runtime.js +507 -507
- package/src/recipes/recipe-store.js +410 -410
- package/SECURITY.md +0 -77
- package/VERSIONING.md +0 -100
- package/guardian-contract-v1.md +0 -502
package/src/guardian/safety.js
CHANGED
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Guardian Safety Guards Module
|
|
3
|
-
* Prevents destructive or dangerous actions during testing
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class GuardianSafety {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
// URL patterns to avoid (logout, delete, admin, etc.)
|
|
9
|
-
this.denyUrlPatterns = options.denyUrlPatterns || [
|
|
10
|
-
'logout',
|
|
11
|
-
'signout',
|
|
12
|
-
'sign-out',
|
|
13
|
-
'log-out',
|
|
14
|
-
'delete',
|
|
15
|
-
'remove',
|
|
16
|
-
'destroy',
|
|
17
|
-
'admin',
|
|
18
|
-
'settings',
|
|
19
|
-
'account/close',
|
|
20
|
-
'account/delete',
|
|
21
|
-
'unsubscribe',
|
|
22
|
-
'cancel',
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
// CSS selectors to avoid clicking
|
|
26
|
-
this.denySelectors = options.denySelectors || [
|
|
27
|
-
'[data-danger]',
|
|
28
|
-
'[data-destructive]',
|
|
29
|
-
'.btn-delete',
|
|
30
|
-
'.btn-danger',
|
|
31
|
-
'.delete-button',
|
|
32
|
-
'button[type="reset"]',
|
|
33
|
-
'a[href*="logout"]',
|
|
34
|
-
'a[href*="delete"]',
|
|
35
|
-
'a[href*="remove"]',
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// Form submissions require explicit permission
|
|
39
|
-
this.blockFormSubmitsByDefault = options.blockFormSubmitsByDefault !== false;
|
|
40
|
-
|
|
41
|
-
// Payment-related actions require explicit permission
|
|
42
|
-
this.blockPaymentsByDefault = options.blockPaymentsByDefault !== false;
|
|
43
|
-
|
|
44
|
-
// Payment-related keywords
|
|
45
|
-
this.paymentKeywords = [
|
|
46
|
-
'payment',
|
|
47
|
-
'checkout',
|
|
48
|
-
'purchase',
|
|
49
|
-
'buy',
|
|
50
|
-
'pay',
|
|
51
|
-
'card',
|
|
52
|
-
'billing',
|
|
53
|
-
'stripe',
|
|
54
|
-
'paypal',
|
|
55
|
-
];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Check if URL is safe to visit
|
|
60
|
-
* @param {string} url - URL to check
|
|
61
|
-
* @returns {object} { safe: boolean, reason: string }
|
|
62
|
-
*/
|
|
63
|
-
isUrlSafe(url) {
|
|
64
|
-
try {
|
|
65
|
-
const urlLower = url.toLowerCase();
|
|
66
|
-
|
|
67
|
-
for (const pattern of this.denyUrlPatterns) {
|
|
68
|
-
if (urlLower.includes(pattern.toLowerCase())) {
|
|
69
|
-
return {
|
|
70
|
-
safe: false,
|
|
71
|
-
reason: `URL contains blocked pattern: "${pattern}"`,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return { safe: true, reason: null };
|
|
77
|
-
} catch (error) {
|
|
78
|
-
return {
|
|
79
|
-
safe: false,
|
|
80
|
-
reason: `Invalid URL: ${error.message}`,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Check if selector is safe to click
|
|
87
|
-
* @param {string} selector - CSS selector
|
|
88
|
-
* @returns {object} { safe: boolean, reason: string }
|
|
89
|
-
*/
|
|
90
|
-
isSelectorSafe(selector) {
|
|
91
|
-
try {
|
|
92
|
-
const selectorLower = selector.toLowerCase();
|
|
93
|
-
|
|
94
|
-
for (const denyPattern of this.denySelectors) {
|
|
95
|
-
if (selectorLower.includes(denyPattern.toLowerCase())) {
|
|
96
|
-
return {
|
|
97
|
-
safe: false,
|
|
98
|
-
reason: `Selector matches blocked pattern: "${denyPattern}"`,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return { safe: true, reason: null };
|
|
104
|
-
} catch (error) {
|
|
105
|
-
return {
|
|
106
|
-
safe: false,
|
|
107
|
-
reason: `Invalid selector: ${error.message}`,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Check if element text suggests dangerous action
|
|
114
|
-
* @param {string} text - Element text content
|
|
115
|
-
* @returns {object} { safe: boolean, reason: string }
|
|
116
|
-
*/
|
|
117
|
-
isTextSafe(text) {
|
|
118
|
-
if (!text) {
|
|
119
|
-
return { safe: true, reason: null };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const textLower = text.toLowerCase().trim();
|
|
123
|
-
const dangerousWords = [
|
|
124
|
-
'logout',
|
|
125
|
-
'log out',
|
|
126
|
-
'sign out',
|
|
127
|
-
'delete',
|
|
128
|
-
'remove',
|
|
129
|
-
'destroy',
|
|
130
|
-
'cancel account',
|
|
131
|
-
'close account',
|
|
132
|
-
'unsubscribe',
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
for (const word of dangerousWords) {
|
|
136
|
-
if (textLower.includes(word)) {
|
|
137
|
-
return {
|
|
138
|
-
safe: false,
|
|
139
|
-
reason: `Text contains dangerous word: "${word}"`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return { safe: true, reason: null };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Check if action involves payment
|
|
149
|
-
* @param {string} context - Context (URL, selector, or text)
|
|
150
|
-
* @returns {boolean} True if payment-related
|
|
151
|
-
*/
|
|
152
|
-
isPaymentRelated(context) {
|
|
153
|
-
if (!context) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const contextLower = context.toLowerCase();
|
|
158
|
-
|
|
159
|
-
for (const keyword of this.paymentKeywords) {
|
|
160
|
-
if (contextLower.includes(keyword)) {
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Check if form submission is safe
|
|
170
|
-
* @param {string} formAction - Form action URL or selector
|
|
171
|
-
* @param {object} formData - Form data being submitted
|
|
172
|
-
* @returns {object} { safe: boolean, reason: string }
|
|
173
|
-
*/
|
|
174
|
-
isFormSubmitSafe(formAction, formData = {}) {
|
|
175
|
-
// Check if form submissions are globally blocked
|
|
176
|
-
if (this.blockFormSubmitsByDefault) {
|
|
177
|
-
return {
|
|
178
|
-
safe: false,
|
|
179
|
-
reason: 'Form submissions are blocked by default (safety guard)',
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Check if form action URL is safe
|
|
184
|
-
if (formAction) {
|
|
185
|
-
const urlCheck = this.isUrlSafe(formAction);
|
|
186
|
-
if (!urlCheck.safe) {
|
|
187
|
-
return urlCheck;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Check if payment-related
|
|
191
|
-
if (this.blockPaymentsByDefault && this.isPaymentRelated(formAction)) {
|
|
192
|
-
return {
|
|
193
|
-
safe: false,
|
|
194
|
-
reason: 'Form submission appears payment-related (blocked by safety guard)',
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Check form data for sensitive fields
|
|
200
|
-
const formDataStr = JSON.stringify(formData).toLowerCase();
|
|
201
|
-
if (this.blockPaymentsByDefault && this.isPaymentRelated(formDataStr)) {
|
|
202
|
-
return {
|
|
203
|
-
safe: false,
|
|
204
|
-
reason: 'Form data contains payment-related fields (blocked by safety guard)',
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return { safe: true, reason: null };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Filter URLs to remove unsafe ones
|
|
213
|
-
* @param {string[]} urls - Array of URLs
|
|
214
|
-
* @returns {object} { safe: string[], blocked: Array<{url, reason}> }
|
|
215
|
-
*/
|
|
216
|
-
filterUrls(urls) {
|
|
217
|
-
const safe = [];
|
|
218
|
-
const blocked = [];
|
|
219
|
-
|
|
220
|
-
for (const url of urls) {
|
|
221
|
-
const check = this.isUrlSafe(url);
|
|
222
|
-
if (check.safe) {
|
|
223
|
-
safe.push(url);
|
|
224
|
-
} else {
|
|
225
|
-
blocked.push({ url, reason: check.reason });
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return { safe, blocked };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Get safety summary (how many URLs/actions were blocked)
|
|
234
|
-
* @param {object} stats - Statistics object
|
|
235
|
-
* @returns {object} Safety summary
|
|
236
|
-
*/
|
|
237
|
-
getSummary(stats = {}) {
|
|
238
|
-
return {
|
|
239
|
-
urlsBlocked: stats.urlsBlocked || 0,
|
|
240
|
-
selectorsBlocked: stats.selectorsBlocked || 0,
|
|
241
|
-
formsBlocked: stats.formsBlocked || 0,
|
|
242
|
-
totalBlocked: (stats.urlsBlocked || 0) + (stats.selectorsBlocked || 0) + (stats.formsBlocked || 0),
|
|
243
|
-
safetyEnabled: true,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
module.exports = GuardianSafety;
|
|
1
|
+
/**
|
|
2
|
+
* Guardian Safety Guards Module
|
|
3
|
+
* Prevents destructive or dangerous actions during testing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class GuardianSafety {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
// URL patterns to avoid (logout, delete, admin, etc.)
|
|
9
|
+
this.denyUrlPatterns = options.denyUrlPatterns || [
|
|
10
|
+
'logout',
|
|
11
|
+
'signout',
|
|
12
|
+
'sign-out',
|
|
13
|
+
'log-out',
|
|
14
|
+
'delete',
|
|
15
|
+
'remove',
|
|
16
|
+
'destroy',
|
|
17
|
+
'admin',
|
|
18
|
+
'settings',
|
|
19
|
+
'account/close',
|
|
20
|
+
'account/delete',
|
|
21
|
+
'unsubscribe',
|
|
22
|
+
'cancel',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
// CSS selectors to avoid clicking
|
|
26
|
+
this.denySelectors = options.denySelectors || [
|
|
27
|
+
'[data-danger]',
|
|
28
|
+
'[data-destructive]',
|
|
29
|
+
'.btn-delete',
|
|
30
|
+
'.btn-danger',
|
|
31
|
+
'.delete-button',
|
|
32
|
+
'button[type="reset"]',
|
|
33
|
+
'a[href*="logout"]',
|
|
34
|
+
'a[href*="delete"]',
|
|
35
|
+
'a[href*="remove"]',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// Form submissions require explicit permission
|
|
39
|
+
this.blockFormSubmitsByDefault = options.blockFormSubmitsByDefault !== false;
|
|
40
|
+
|
|
41
|
+
// Payment-related actions require explicit permission
|
|
42
|
+
this.blockPaymentsByDefault = options.blockPaymentsByDefault !== false;
|
|
43
|
+
|
|
44
|
+
// Payment-related keywords
|
|
45
|
+
this.paymentKeywords = [
|
|
46
|
+
'payment',
|
|
47
|
+
'checkout',
|
|
48
|
+
'purchase',
|
|
49
|
+
'buy',
|
|
50
|
+
'pay',
|
|
51
|
+
'card',
|
|
52
|
+
'billing',
|
|
53
|
+
'stripe',
|
|
54
|
+
'paypal',
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if URL is safe to visit
|
|
60
|
+
* @param {string} url - URL to check
|
|
61
|
+
* @returns {object} { safe: boolean, reason: string }
|
|
62
|
+
*/
|
|
63
|
+
isUrlSafe(url) {
|
|
64
|
+
try {
|
|
65
|
+
const urlLower = url.toLowerCase();
|
|
66
|
+
|
|
67
|
+
for (const pattern of this.denyUrlPatterns) {
|
|
68
|
+
if (urlLower.includes(pattern.toLowerCase())) {
|
|
69
|
+
return {
|
|
70
|
+
safe: false,
|
|
71
|
+
reason: `URL contains blocked pattern: "${pattern}"`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { safe: true, reason: null };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
safe: false,
|
|
80
|
+
reason: `Invalid URL: ${error.message}`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if selector is safe to click
|
|
87
|
+
* @param {string} selector - CSS selector
|
|
88
|
+
* @returns {object} { safe: boolean, reason: string }
|
|
89
|
+
*/
|
|
90
|
+
isSelectorSafe(selector) {
|
|
91
|
+
try {
|
|
92
|
+
const selectorLower = selector.toLowerCase();
|
|
93
|
+
|
|
94
|
+
for (const denyPattern of this.denySelectors) {
|
|
95
|
+
if (selectorLower.includes(denyPattern.toLowerCase())) {
|
|
96
|
+
return {
|
|
97
|
+
safe: false,
|
|
98
|
+
reason: `Selector matches blocked pattern: "${denyPattern}"`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { safe: true, reason: null };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
safe: false,
|
|
107
|
+
reason: `Invalid selector: ${error.message}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if element text suggests dangerous action
|
|
114
|
+
* @param {string} text - Element text content
|
|
115
|
+
* @returns {object} { safe: boolean, reason: string }
|
|
116
|
+
*/
|
|
117
|
+
isTextSafe(text) {
|
|
118
|
+
if (!text) {
|
|
119
|
+
return { safe: true, reason: null };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const textLower = text.toLowerCase().trim();
|
|
123
|
+
const dangerousWords = [
|
|
124
|
+
'logout',
|
|
125
|
+
'log out',
|
|
126
|
+
'sign out',
|
|
127
|
+
'delete',
|
|
128
|
+
'remove',
|
|
129
|
+
'destroy',
|
|
130
|
+
'cancel account',
|
|
131
|
+
'close account',
|
|
132
|
+
'unsubscribe',
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
for (const word of dangerousWords) {
|
|
136
|
+
if (textLower.includes(word)) {
|
|
137
|
+
return {
|
|
138
|
+
safe: false,
|
|
139
|
+
reason: `Text contains dangerous word: "${word}"`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { safe: true, reason: null };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Check if action involves payment
|
|
149
|
+
* @param {string} context - Context (URL, selector, or text)
|
|
150
|
+
* @returns {boolean} True if payment-related
|
|
151
|
+
*/
|
|
152
|
+
isPaymentRelated(context) {
|
|
153
|
+
if (!context) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const contextLower = context.toLowerCase();
|
|
158
|
+
|
|
159
|
+
for (const keyword of this.paymentKeywords) {
|
|
160
|
+
if (contextLower.includes(keyword)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if form submission is safe
|
|
170
|
+
* @param {string} formAction - Form action URL or selector
|
|
171
|
+
* @param {object} formData - Form data being submitted
|
|
172
|
+
* @returns {object} { safe: boolean, reason: string }
|
|
173
|
+
*/
|
|
174
|
+
isFormSubmitSafe(formAction, formData = {}) {
|
|
175
|
+
// Check if form submissions are globally blocked
|
|
176
|
+
if (this.blockFormSubmitsByDefault) {
|
|
177
|
+
return {
|
|
178
|
+
safe: false,
|
|
179
|
+
reason: 'Form submissions are blocked by default (safety guard)',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check if form action URL is safe
|
|
184
|
+
if (formAction) {
|
|
185
|
+
const urlCheck = this.isUrlSafe(formAction);
|
|
186
|
+
if (!urlCheck.safe) {
|
|
187
|
+
return urlCheck;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check if payment-related
|
|
191
|
+
if (this.blockPaymentsByDefault && this.isPaymentRelated(formAction)) {
|
|
192
|
+
return {
|
|
193
|
+
safe: false,
|
|
194
|
+
reason: 'Form submission appears payment-related (blocked by safety guard)',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check form data for sensitive fields
|
|
200
|
+
const formDataStr = JSON.stringify(formData).toLowerCase();
|
|
201
|
+
if (this.blockPaymentsByDefault && this.isPaymentRelated(formDataStr)) {
|
|
202
|
+
return {
|
|
203
|
+
safe: false,
|
|
204
|
+
reason: 'Form data contains payment-related fields (blocked by safety guard)',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { safe: true, reason: null };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Filter URLs to remove unsafe ones
|
|
213
|
+
* @param {string[]} urls - Array of URLs
|
|
214
|
+
* @returns {object} { safe: string[], blocked: Array<{url, reason}> }
|
|
215
|
+
*/
|
|
216
|
+
filterUrls(urls) {
|
|
217
|
+
const safe = [];
|
|
218
|
+
const blocked = [];
|
|
219
|
+
|
|
220
|
+
for (const url of urls) {
|
|
221
|
+
const check = this.isUrlSafe(url);
|
|
222
|
+
if (check.safe) {
|
|
223
|
+
safe.push(url);
|
|
224
|
+
} else {
|
|
225
|
+
blocked.push({ url, reason: check.reason });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { safe, blocked };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get safety summary (how many URLs/actions were blocked)
|
|
234
|
+
* @param {object} stats - Statistics object
|
|
235
|
+
* @returns {object} Safety summary
|
|
236
|
+
*/
|
|
237
|
+
getSummary(stats = {}) {
|
|
238
|
+
return {
|
|
239
|
+
urlsBlocked: stats.urlsBlocked || 0,
|
|
240
|
+
selectorsBlocked: stats.selectorsBlocked || 0,
|
|
241
|
+
formsBlocked: stats.formsBlocked || 0,
|
|
242
|
+
totalBlocked: (stats.urlsBlocked || 0) + (stats.selectorsBlocked || 0) + (stats.formsBlocked || 0),
|
|
243
|
+
safetyEnabled: true,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = GuardianSafety;
|