Populating multiple password dialogs

I’ve written a script that starts by asking for the user’s password, which gets stored in a variable and used to populate multiple password dialogs as they appear; quitting after 2min of no dialogs found. The problem is that only one dialog is ever found, while I can see many more onscreen.

Technically this is JavaScript, but it follows AppleScript:

const GET_CREDENTIALS_WINDOW_INTERVAL = 0.25;
const GET_CREDENTIALS_WINDOW_TIMEOUT = 120;
let app;

class TimedOutError extends Error {
	constructor() {
		super('Timed out looking for a credentials window');
	}
}

const getCredentials = () => ({
	password: app.displayDialog('Enter your password', { defaultAnswer: '', hiddenAnswer: true }).textReturned,
	username: app.systemInfo().shortUserName,
});

let attempt = 0;
const populateCredentialsWindows = (password, duration = 0) => {
	attempt++;
	if (duration >= GET_CREDENTIALS_WINDOW_TIMEOUT) {
		throw new TimedOutError();
	}

	const systemEvents = Application('System Events');
	
	systemEvents
		.processes()
		.filter(process => process.name() === 'SecurityAgent')
		// Not sure if there's ever more than one process, but this should cover such
		.forEach((process, _, { length: numProcesses }) => {
			console.log(`(${attempt}) numProcesses: ${numProcesses}`);
			process.windows().forEach((win, _, { length: numWindows }) => {
				// Avoids issue where subsequent windows wouldn't get focused
				//delay(GET_CREDENTIALS_WINDOW_INTERVAL);
				//systemEvents.activate();
				//app.frontmost = true;
				//process.frontmost = true;
				//win.actions['AXRaise'].perform();

				console.log(`(${attempt}) numWindows: ${numWindows}`);

				win.textFields[0].value = password;
				win.buttons['Allow'].click();
			
				// Reset timeout
				duration = 0;
			});
		});

	delay(GET_CREDENTIALS_WINDOW_INTERVAL);
	duration += GET_CREDENTIALS_WINDOW_INTERVAL;
	populateCredentialsWindows(password, duration);
};

const run = () => {
	app = Application.currentApplication();
	app.includeStandardAdditions = true;

	const { password } = getCredentials();

	try {
		return populateCredentialsWindows(password);
	} catch (error) {
		if (!(error instanceof TimedOutError)) {
			throw error;
		}
	}
};

run();

AppleScript: 2.7
Browser: Safari 537.36
Operating System: macOS 10.14

All is simpler when using AppleScript instead of JXA. JXA has many bugs and is abandoned as well. Run following script within Script Editor or as application:


property GET_CREDENTIALS_WINDOW_INTERVAL : 2
property GET_CREDENTIALS_WINDOW_TIMEOUT : 120

set myPassword to text returned of (display dialog "Enter your password" default answer "" giving up after GET_CREDENTIALS_WINDOW_TIMEOUT with hidden answer)
if myPassword = "" then error number -1712

tell application "System Events" to tell process "SecurityAgent"
	repeat (count of windows) times
		set frontmost to true
		keystroke myPassword & return
		delay GET_CREDENTIALS_WINDOW_INTERVAL
	end repeat
end tell

Thanks! Although, I’m getting:

Also, the 2min timeout is for the absence of the last SecurityAgent dialog, not the initial input from the script.

I’ll try a few things, but I’m really new to AppleScript.

You get this error because no any security dialog opened by you before running my script.

To test do following: create 2 empty folders on the desktop, make them read-only, then lock them, then right click them to trash. Will pop up 2 security prompts to enter the password. Run my script.

I’m trying to write a script that can be ran before the popups appear, as they will continue to appear during a complicated build/compile.

This won’t populate anything:


property GET_INTERVAL : 2
property INACTIVITY_TIMEOUT : 120
property TIMEOUT_ERROR : -1712

set userPassword to text returned of (display dialog "Enter your password" default answer "" with hidden answer)

tell application "System Events"
	set inactivityDuration to 0
	
	repeat until exists process "SecurityAgent"
		delay GET_INTERVAL
		set inactivityDuration to inactivityDuration + GET_INTERVAL
		if inactivityDuration ≥ INACTIVITY_TIMEOUT then error number TIMEOUT_ERROR
	end repeat
	
	# Reset
	set inactivityDuration to 0
	
	repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT
		if (count of windows) = 0 then
			delay GET_INTERVAL
			set inactivityDuration to inactivityDuration + GET_INTERVAL
		else
			tell process "SecurityAgent"
				repeat (count of windows) times
					set frontmost to true
					keystroke userPassword & return
					delay GET_INTERVAL
				end repeat
			end tell
			
			# Reset
			set inactivityDuration to 0
		end if
	end repeat
	
	error number TIMEOUT_ERROR
end tell

if (count of windows of process "SecurityAgent") = 0 then -- EDITED

and add

set frontmost of process "SecurityAgent" to frontmost

before

repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT line

Thanks, that makes it at least sort of work. This populates the first two dialogs, which is better than the JavaScript version which only populated the first. Any subsequent dialogs are not getting populated. I think it’s because SecurityAgent eventually closes when there’re no windows

Yes.

Simplified:


property GET_INTERVAL : 2
property INACTIVITY_TIMEOUT : 120
property TIMEOUT_ERROR : -1712

set userPassword to text returned of (display dialog "Enter your password" default answer "" with hidden answer)

tell application "System Events"
	set inactivityDuration to 0
	
	repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT
		if not (exists process "SecurityAgent") or (count of windows of process "SecurityAgent") = 0 then
			delay GET_INTERVAL
			set inactivityDuration to inactivityDuration + GET_INTERVAL
		else
			tell process "SecurityAgent"
				#activate
				#set frontmost to true
				repeat with win in windows
					set frontmost to true
					#perform action "AXRaise" of win
					#click at {100, 100}
					#delay 0.1
					#keystroke userPassword & return
					keystroke userPassword
					#delay 0.1
					keystroke return
					delay GET_INTERVAL
				end repeat
				
				# Reset
				set inactivityDuration to 0
			end tell
		end if
	end repeat
	
	# Done
	error number TIMEOUT_ERROR
end tell

… but only ever finds one of the many visible password dialogs:

… before repeating this:

I can get it to work by manually clicking the window, entering a single character into the password field and selecting all text in it so that it can be replaced by the script. This is obviously not ideal.