@skylandnpm/octopus-proxy 0.1.39 → 0.1.40
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/dist/{chunk-BYXUILAK.js → chunk-OXA2S7UY.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/main.js +1 -1
- package/native/screen-unlock-macos.m +35 -188
- package/package.json +1 -1
|
@@ -12359,8 +12359,8 @@ var ScreenLockError = class extends Error {
|
|
|
12359
12359
|
var nativeErrors = {
|
|
12360
12360
|
permission: "\u8BF7\u5728 Proxy \u4E3B\u673A\u201C\u7CFB\u7EDF\u8BBE\u7F6E \u2192 \u9690\u79C1\u4E0E\u5B89\u5168\u6027 \u2192 \u8F85\u52A9\u529F\u80FD\u201D\u4E2D\u6388\u6743\u5B9E\u9645\u8FD0\u884C Proxy \u7684\u7A0B\u5E8F\uFF0C\u5E76\u91CD\u542F Proxy\u3002",
|
|
12361
12361
|
session: "\u65E0\u6CD5\u8BBF\u95EE Proxy \u7528\u6237\u7684\u6D3B\u52A8\u56FE\u5F62\u4F1A\u8BDD\uFF1B\u4E0D\u652F\u6301\u767B\u5F55\u524D\u3001FileVault \u542F\u52A8\u89E3\u9501\u6216\u5207\u6362\u5230\u5176\u4ED6\u7528\u6237\u7684\u5C4F\u5E55\u3002",
|
|
12362
|
-
focus: "\
|
|
12363
|
-
changed: "\u4E3B\u673A\u9501\u5C4F\u72B6\u6001\u6216\
|
|
12362
|
+
focus: "\u65E0\u6CD5\u5411\u4E3B\u673A\u9501\u5C4F\u754C\u9762\u53D1\u9001\u5524\u9192\u6309\u952E\uFF0C\u5C1A\u672A\u8F93\u5165\u5BC6\u7801\u3002\u8BF7\u68C0\u67E5\u4E3B\u673A\u6D3B\u52A8\u56FE\u5F62\u4F1A\u8BDD\u540E\u91CD\u8BD5\u3002",
|
|
12363
|
+
changed: "\u4E3B\u673A\u9501\u5C4F\u72B6\u6001\u6216\u6D3B\u52A8\u4F1A\u8BDD\u5DF2\u53D8\u5316\uFF0C\u5DF2\u505C\u6B62\u8F93\u5165\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
12364
12364
|
timeout: "\u4E3B\u673A\u5C4F\u5E55\u4ECD\u9501\u5B9A\uFF0C\u7CFB\u7EDF\u672A\u63A5\u53D7\u89E3\u9501\u8F93\u5165\uFF1B\u8BF7\u68C0\u67E5\u8F85\u52A9\u529F\u80FD\u6743\u9650\u548C\u9501\u5C4F\u754C\u9762\u540E\u91CD\u8BD5\u3002",
|
|
12365
12365
|
password: "\u4E3B\u673A\u5BC6\u7801\u683C\u5F0F\u65E0\u6548\u3002"
|
|
12366
12366
|
};
|
package/dist/cli.js
CHANGED
package/dist/main.js
CHANGED
|
@@ -57,142 +57,6 @@ static pid_t loginWindowPID(void) {
|
|
|
57
57
|
return cached;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
static CFTypeRef copyAttribute(AXUIElementRef element, CFStringRef attribute) {
|
|
61
|
-
CFTypeRef value = NULL;
|
|
62
|
-
AXUIElementSetMessagingTimeout(element, 0.1);
|
|
63
|
-
if (AXUIElementCopyAttributeValue(element, attribute, &value) != kAXErrorSuccess) {
|
|
64
|
-
if (value) CFRelease(value);
|
|
65
|
-
return NULL;
|
|
66
|
-
}
|
|
67
|
-
return value;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static BOOL isSecureField(AXUIElementRef field) {
|
|
71
|
-
if (!field || CFGetTypeID(field) != AXUIElementGetTypeID()) return NO;
|
|
72
|
-
pid_t pid = 0;
|
|
73
|
-
if (AXUIElementGetPid(field, &pid) != kAXErrorSuccess || !isLoginWindow(pid)) return NO;
|
|
74
|
-
CFTypeRef role = copyAttribute(field, kAXRoleAttribute);
|
|
75
|
-
CFTypeRef subrole = copyAttribute(field, kAXSubroleAttribute);
|
|
76
|
-
BOOL secure = (subrole && CFEqual(subrole, kAXSecureTextFieldSubrole)) ||
|
|
77
|
-
(role && CFEqual(role, kAXSecureTextFieldSubrole));
|
|
78
|
-
if (role) CFRelease(role);
|
|
79
|
-
if (subrole) CFRelease(subrole);
|
|
80
|
-
CFTypeRef enabled = copyAttribute(field, kAXEnabledAttribute);
|
|
81
|
-
if (enabled && CFEqual(enabled, kCFBooleanFalse)) secure = NO;
|
|
82
|
-
if (enabled) CFRelease(enabled);
|
|
83
|
-
CFTypeRef hidden = copyAttribute(field, CFSTR("AXHidden"));
|
|
84
|
-
if (hidden && CFEqual(hidden, kCFBooleanTrue)) secure = NO;
|
|
85
|
-
if (hidden) CFRelease(hidden);
|
|
86
|
-
return secure;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static AXUIElementRef copySecureFocus(AXUIElementRef root) {
|
|
90
|
-
CFTypeRef focused = copyAttribute(root, kAXFocusedUIElementAttribute);
|
|
91
|
-
if (focused && isSecureField((AXUIElementRef)focused)) return (AXUIElementRef)focused;
|
|
92
|
-
if (focused) CFRelease(focused);
|
|
93
|
-
return NULL;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
static void collectSecureFields(AXUIElementRef root, CFMutableArrayRef fields,
|
|
97
|
-
CFMutableSetRef visited, int depth, CFAbsoluteTime deadline) {
|
|
98
|
-
if (!root || CFGetTypeID(root) != AXUIElementGetTypeID() || depth > 10 ||
|
|
99
|
-
CFSetGetCount(visited) >= 128 || CFAbsoluteTimeGetCurrent() >= deadline || CFSetContainsValue(visited, root)) return;
|
|
100
|
-
CFSetAddValue(visited, root);
|
|
101
|
-
if (isSecureField(root)) { CFArrayAppendValue(fields, root); return; }
|
|
102
|
-
// Some loginwindow versions expose windows separately from AXChildren.
|
|
103
|
-
CFStringRef attributes[] = { kAXChildrenAttribute, kAXWindowsAttribute };
|
|
104
|
-
for (int i = 0; i < 2; i++) {
|
|
105
|
-
CFTypeRef children = copyAttribute(root, attributes[i]);
|
|
106
|
-
if (children && CFGetTypeID(children) == CFArrayGetTypeID()) {
|
|
107
|
-
for (CFIndex j = 0; j < CFArrayGetCount(children); j++)
|
|
108
|
-
collectSecureFields((AXUIElementRef)CFArrayGetValueAtIndex(children, j), fields, visited, depth + 1, deadline);
|
|
109
|
-
}
|
|
110
|
-
if (children) CFRelease(children);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
static CFArrayRef copySecureFields(AXUIElementRef app) {
|
|
115
|
-
CFMutableArrayRef fields = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
|
116
|
-
CFMutableSetRef visited = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
|
|
117
|
-
collectSecureFields(app, fields, visited, 0, CFAbsoluteTimeGetCurrent() + 2);
|
|
118
|
-
CFRelease(visited);
|
|
119
|
-
return fields;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
static AXUIElementRef passwordFocus(void) {
|
|
123
|
-
AXUIElementRef system = AXUIElementCreateSystemWide();
|
|
124
|
-
AXUIElementRef field = copySecureFocus(system);
|
|
125
|
-
CFRelease(system);
|
|
126
|
-
if (field) return field;
|
|
127
|
-
pid_t pid = loginWindowPID();
|
|
128
|
-
if (!pid) return NULL;
|
|
129
|
-
AXUIElementRef app = AXUIElementCreateApplication(pid);
|
|
130
|
-
field = copySecureFocus(app);
|
|
131
|
-
if (!field) {
|
|
132
|
-
// A locked console can lack the system-wide focused element while the
|
|
133
|
-
// password field itself or loginwindow still reports its local focus.
|
|
134
|
-
CFArrayRef fields = copySecureFields(app);
|
|
135
|
-
for (CFIndex i = 0; i < CFArrayGetCount(fields); i++) {
|
|
136
|
-
AXUIElementRef candidate = (AXUIElementRef)CFArrayGetValueAtIndex(fields, i);
|
|
137
|
-
CFTypeRef focused = copyAttribute(candidate, kAXFocusedAttribute);
|
|
138
|
-
BOOL active = focused && CFEqual(focused, kCFBooleanTrue);
|
|
139
|
-
if (focused) CFRelease(focused);
|
|
140
|
-
if (!active) continue;
|
|
141
|
-
if (field) { CFRelease(field); field = NULL; break; }
|
|
142
|
-
field = (AXUIElementRef)CFRetain(candidate);
|
|
143
|
-
}
|
|
144
|
-
CFRelease(fields);
|
|
145
|
-
}
|
|
146
|
-
CFRelease(app);
|
|
147
|
-
return field;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
static AXUIElementRef focusPasswordField(void) {
|
|
151
|
-
BOOL locked = NO;
|
|
152
|
-
if (!lockState(&locked) || !locked) return NULL;
|
|
153
|
-
AXUIElementRef focused = passwordFocus();
|
|
154
|
-
if (focused) return focused;
|
|
155
|
-
pid_t pid = loginWindowPID();
|
|
156
|
-
if (!pid) return NULL;
|
|
157
|
-
AXUIElementRef app = AXUIElementCreateApplication(pid);
|
|
158
|
-
CFArrayRef fields = copySecureFields(app);
|
|
159
|
-
CFRelease(app);
|
|
160
|
-
// Never guess between multiple password fields or use a generic text field.
|
|
161
|
-
if (CFArrayGetCount(fields) != 1) { CFRelease(fields); return NULL; }
|
|
162
|
-
AXUIElementRef field = (AXUIElementRef)CFArrayGetValueAtIndex(fields, 0);
|
|
163
|
-
if (!lockState(&locked) || !locked || !isSecureField(field)) { CFRelease(fields); return NULL; }
|
|
164
|
-
AXError activated = AXUIElementSetAttributeValue(field, kAXFocusedAttribute, kCFBooleanTrue);
|
|
165
|
-
if (activated != kAXErrorSuccess) AXUIElementPerformAction(field, kAXPressAction);
|
|
166
|
-
usleep(50000);
|
|
167
|
-
focused = passwordFocus();
|
|
168
|
-
if (focused && !CFEqual(field, focused)) { CFRelease(focused); focused = NULL; }
|
|
169
|
-
CFRelease(fields);
|
|
170
|
-
return focused;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
static BOOL stillFocused(AXUIElementRef field) {
|
|
174
|
-
BOOL locked = NO;
|
|
175
|
-
if (!lockState(&locked) || !locked || !isSecureField(field)) return NO;
|
|
176
|
-
AXUIElementRef system = AXUIElementCreateSystemWide();
|
|
177
|
-
AXUIElementRef current = copySecureFocus(system);
|
|
178
|
-
CFRelease(system);
|
|
179
|
-
if (!current) {
|
|
180
|
-
pid_t pid = 0;
|
|
181
|
-
if (AXUIElementGetPid(field, &pid) != kAXErrorSuccess) return NO;
|
|
182
|
-
AXUIElementRef app = AXUIElementCreateApplication(pid);
|
|
183
|
-
current = copySecureFocus(app);
|
|
184
|
-
CFRelease(app);
|
|
185
|
-
}
|
|
186
|
-
BOOL same = current && CFEqual(current, field);
|
|
187
|
-
if (current) { CFRelease(current); return same; }
|
|
188
|
-
// Avoid traversing the entire AX tree per character on systems that only
|
|
189
|
-
// expose field-local focus. A false or unavailable focus is never accepted.
|
|
190
|
-
CFTypeRef focused = copyAttribute(field, kAXFocusedAttribute);
|
|
191
|
-
same = focused && CFEqual(focused, kCFBooleanTrue);
|
|
192
|
-
if (focused) CFRelease(focused);
|
|
193
|
-
return same;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
60
|
static BOOL key(CGKeyCode code, CGEventFlags flags, const UniChar *text, size_t length) {
|
|
197
61
|
CGEventRef down = CGEventCreateKeyboardEvent(NULL, code, true);
|
|
198
62
|
CGEventRef up = CGEventCreateKeyboardEvent(NULL, code, false);
|
|
@@ -205,34 +69,32 @@ static BOOL key(CGKeyCode code, CGEventFlags flags, const UniChar *text, size_t
|
|
|
205
69
|
return YES;
|
|
206
70
|
}
|
|
207
71
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
BOOL appSecure = NO;
|
|
221
|
-
if (pid && AXIsProcessTrusted()) {
|
|
222
|
-
AXUIElementRef app = AXUIElementCreateApplication(pid);
|
|
223
|
-
AXUIElementSetMessagingTimeout(app, 0.1);
|
|
224
|
-
CFTypeRef local = NULL;
|
|
225
|
-
appError = AXUIElementCopyAttributeValue(app, kAXFocusedUIElementAttribute, &local);
|
|
226
|
-
appSecure = local && isSecureField((AXUIElementRef)local);
|
|
227
|
-
if (local) CFRelease(local);
|
|
228
|
-
CFArrayRef fields = copySecureFields(app);
|
|
229
|
-
count = CFArrayGetCount(fields);
|
|
230
|
-
CFRelease(fields); CFRelease(app);
|
|
72
|
+
// The locked console owns keyboard input. Space reveals the password prompt,
|
|
73
|
+
// and Backspace removes that Space if the field was already visible. Do not
|
|
74
|
+
// depend on loginwindow exposing an Accessibility tree (some versions do not).
|
|
75
|
+
static BOOL wakePasswordPrompt(void) {
|
|
76
|
+
const CGKeyCode keys[] = {56, 49, 51};
|
|
77
|
+
const useconds_t settle[] = {1000000, 500000, 200000};
|
|
78
|
+
for (int i = 0; i < 3; i++) {
|
|
79
|
+
BOOL locked = NO;
|
|
80
|
+
if (!lockState(&locked)) return NO;
|
|
81
|
+
if (!locked) return YES;
|
|
82
|
+
if (!key(keys[i], 0, NULL, 0)) return NO;
|
|
83
|
+
usleep(settle[i]);
|
|
231
84
|
}
|
|
232
|
-
|
|
85
|
+
return YES;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static BOOL lockedKey(CGKeyCode code, CGEventFlags flags, const UniChar *text, size_t length) {
|
|
89
|
+
BOOL locked = NO;
|
|
90
|
+
return lockState(&locked) && locked && key(code, flags, text, length);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static void printDiagnostics(BOOL locked) {
|
|
94
|
+
// Read-only; no wake, focus, field contents, screenshots, or input events.
|
|
95
|
+
printf("{\"locked\":%s,\"canUnlock\":%s,\"loginwindowFound\":%s,\"inputMethod\":\"space-backspace\"}",
|
|
233
96
|
locked ? "true" : "false", AXIsProcessTrusted() && CGPreflightPostEventAccess() ? "true" : "false",
|
|
234
|
-
|
|
235
|
-
appSecure ? "true" : "false", (long)count);
|
|
97
|
+
loginWindowPID() ? "true" : "false");
|
|
236
98
|
}
|
|
237
99
|
|
|
238
100
|
int main(int argc, const char *argv[]) {
|
|
@@ -252,42 +114,27 @@ int main(int argc, const char *argv[]) {
|
|
|
252
114
|
NSString *password = [[NSString alloc] initWithBytes:bytes length:count encoding:NSUTF8StringEncoding];
|
|
253
115
|
wipe(bytes, sizeof(bytes));
|
|
254
116
|
if (!password || [password rangeOfCharacterFromSet:NSCharacterSet.controlCharacterSet].location != NSNotFound) return fail("password");
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (!
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Shift wakes the display but can leave the clock/wallpaper layer visible.
|
|
262
|
-
// Space reveals the sign-in controls without submitting a password.
|
|
263
|
-
if (!field) {
|
|
264
|
-
if (!lockState(&locked) || !locked) return fail("changed");
|
|
265
|
-
if (!key(49, 0, NULL, 0)) return fail("focus");
|
|
266
|
-
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:6];
|
|
267
|
-
while (!field && deadline.timeIntervalSinceNow > 0) {
|
|
268
|
-
usleep(100000);
|
|
269
|
-
field = focusPasswordField();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (!field) return fail("focus");
|
|
273
|
-
if (!stillFocused(field) || !key(0, kCGEventFlagMaskCommand, NULL, 0) ||
|
|
274
|
-
!stillFocused(field) || !key(51, 0, NULL, 0)) { CFRelease(field); return fail("changed"); }
|
|
117
|
+
if (!wakePasswordPrompt()) return fail(lockState(&locked) ? "focus" : "session");
|
|
118
|
+
if (!lockState(&locked)) return fail("session");
|
|
119
|
+
// Saver dismissal, Touch ID or a manual unlock may finish during wake.
|
|
120
|
+
if (!locked) { printStatus(NO); return 0; }
|
|
121
|
+
if (!lockedKey(0, kCGEventFlagMaskCommand, NULL, 0) ||
|
|
122
|
+
!lockedKey(51, 0, NULL, 0)) return fail("changed");
|
|
275
123
|
UniChar characters[1024] = {0};
|
|
276
124
|
NSUInteger length = password.length;
|
|
277
|
-
if (length > 1024)
|
|
125
|
+
if (length > 1024) return fail("password");
|
|
278
126
|
[password getCharacters:characters range:NSMakeRange(0, length)];
|
|
279
127
|
password = nil;
|
|
280
128
|
BOOL sent = YES;
|
|
281
|
-
// One Unicode scalar per event avoids event-string truncation.
|
|
282
|
-
//
|
|
129
|
+
// One Unicode scalar per event avoids event-string truncation. Verify the
|
|
130
|
+
// same user's locked console immediately before every password event.
|
|
283
131
|
for (NSUInteger i = 0; i < length;) {
|
|
284
132
|
NSUInteger units = CFStringIsSurrogateHighCharacter(characters[i]) && i + 1 < length && CFStringIsSurrogateLowCharacter(characters[i + 1]) ? 2 : 1;
|
|
285
|
-
if (!
|
|
133
|
+
if (!lockedKey(0, 0, &characters[i], units)) { sent = NO; break; }
|
|
286
134
|
i += units;
|
|
287
135
|
}
|
|
288
136
|
wipe(characters, sizeof(characters));
|
|
289
|
-
if (!sent || !
|
|
290
|
-
CFRelease(field);
|
|
137
|
+
if (!sent || !lockedKey(36, 0, NULL, 0)) return fail("changed");
|
|
291
138
|
for (int i = 0; i < 50; i++) {
|
|
292
139
|
usleep(100000);
|
|
293
140
|
if (!lockState(&locked)) return fail("session");
|