-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.js
More file actions
270 lines (232 loc) · 7.82 KB
/
content.js
File metadata and controls
270 lines (232 loc) · 7.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/**
* 在頁面上檢測密碼欄位並提供強度改進的提示詞
* 自動檢測頁面中的密碼欄位 並在用戶輸入時提供即時密碼強度評估
* 可以手動控制開啟或關閉檢測功能
*/
class PagePasswordChecker {
constructor() {
this.isEnabled = true;
this.checkedFields = new Set();
this.init();
}
/**
* 初始化
* 在頁面loading完成時自動執行
* 立即檢測頁面中新的密碼欄位
*/
init() {
this.observePasswordFields();
// 檢測新密碼欄位
this.scanExistingFields();
}
/**
* 監聽密碼欄位的變化
* 當有新的密碼欄位被添加到頁面時,自動檢測並設置強度指示器
* 這樣可以確保即使是動態生成的密碼欄位也能被檢測到
*/
observePasswordFields() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.checkForPasswordFields(node);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// 掃描現有欄位
scanExistingFields() {
this.checkForPasswordFields(document);
}
// 檢查密碼欄位
checkForPasswordFields(element) {
if (!this.isEnabled) return;
const passwordFields = element.querySelectorAll ?
element.querySelectorAll('input[type="password"]') :
(element.type === 'password' ? [element] : []);
passwordFields.forEach(field => {
if (!this.checkedFields.has(field)) {
this.setupPasswordField(field);
this.checkedFields.add(field);
}
});
}
setupPasswordField(field) {
// pwd強度indicator
const indicator = this.createStrengthIndicator();
if (field.parentNode) {
field.parentNode.insertBefore(indicator, field.nextSibling);
}
field.addEventListener('input', (e) => {
this.checkPasswordStrength(e.target.value, indicator);
});
field.addEventListener('focus', () => {
indicator.style.display = 'block';
});
field.addEventListener('blur', () => {
setTimeout(() => {
if (!field.value) {
indicator.style.display = 'none';
}
}, 2000);
});
}
// create pwd強度指示器
createStrengthIndicator() {
const container = document.createElement('div');
container.className = 'password-strength-indicator';
container.style.cssText = `
margin-top: 4px;
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: none;
z-index: 1000;
position: relative;
`;
const bar = document.createElement('div');
bar.className = 'strength-bar';
bar.style.cssText = `
width: 100%;
height: 4px;
background-color: #e0e0e0;
border-radius: 2px;
overflow: hidden;
margin-bottom: 4px;
`;
const fill = document.createElement('div');
fill.className = 'strength-fill';
fill.style.cssText = `
height: 100%;
width: 0%;
transition: all 0.3s ease;
border-radius: 2px;
`;
const text = document.createElement('div');
text.className = 'strength-text';
text.style.cssText = `
font-weight: 500;
margin-bottom: 2px;
`;
const tips = document.createElement('div');
tips.className = 'strength-tips';
tips.style.cssText = `
color: #666;
font-size: 11px;
line-height: 1.3;
`;
bar.appendChild(fill);
container.appendChild(bar);
container.appendChild(text);
container.appendChild(tips);
return container;
}
checkPasswordStrength(password, indicator) {
const result = this.calculateStrength(password);
this.updateIndicator(indicator, result);
}
calculateStrength(password) {
if (!password) {
return { score: 0, level: 'empty', text: '', tips: '' };
}
let score = 0;
let tips = [];
// 長度
if (password.length >= 16) score += 25;
else if (password.length >= 12) score += 20;
else if (password.length >= 8) score += 10;
else tips.push('至少8個字符');
// 大寫英文字母
if (/[A-Z]/.test(password)) score += 15;
else tips.push('包含大寫字母');
// 小寫英文字母
if (/[a-z]/.test(password)) score += 15;
else tips.push('包含小寫字母');
// 數字
if (/[0-9]/.test(password)) score += 15;
else tips.push('包含數字');
// 特殊符號
if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\?]/.test(password)) score += 20;
else tips.push('包含特殊符號');
if (/(.)\1{2,}/.test(password)) {
score -= 10;
tips.push('避免重複字符');
}
const uniqueChars = new Set(password).size;
if (uniqueChars >= password.length * 0.8) score += 10;
score = Math.max(0, Math.min(100, score));
// 判斷等級
let level, text, color;
if (score >= 80) {
level = 'strong';
text = '強';
color = '#4caf50';
} else if (score >= 60) {
level = 'good';
text = '良好';
color = '#8bc34a';
} else if (score >= 40) {
level = 'fair';
text = '普通';
color = '#ffeb3b';
} else if (score >= 20) {
level = 'weak';
text = '弱';
color = '#ff9800';
} else {
level = 'very-weak';
text = '非常弱';
color = '#f44336';
}
return {
score,
level,
text,
color,
tips: tips.length > 0 ? '建議: ' + tips.slice(0, 2).join(', ') : '密碼強度優良'
};
}
updateIndicator(indicator, result) {
const fill = indicator.querySelector('.strength-fill');
const text = indicator.querySelector('.strength-text');
const tips = indicator.querySelector('.strength-tips');
fill.style.width = result.score + '%';
fill.style.backgroundColor = result.color;
text.textContent = `密碼強度: ${result.text}`;
text.style.color = result.color;
tips.textContent = result.tips;
}
/**
* 檢測功能開關toggle
* 點擊開關按鈕切換檢測功能啟用stat
*/
toggle() {
this.isEnabled = !this.isEnabled;
const indicators = document.querySelectorAll('.password-strength-indicator');
indicators.forEach(indicator => {
indicator.style.display = this.isEnabled ? 'block' : 'none';
});
}
}
// initalize
let passwordChecker;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
passwordChecker = new PagePasswordChecker();
});
} else {
passwordChecker = new PagePasswordChecker();
}
chrome.runtime.onMessage?.addListener((request, sender, sendResponse) => {
if (request.action === 'toggleChecker') {
passwordChecker?.toggle();
sendResponse({ success: true });
} else if (request.action === 'scanPage') {
passwordChecker?.scanExistingFields();
sendResponse({ success: true });
}
});