Skip to content

Commit

Permalink
improved javascript parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
twocanoes committed May 9, 2023
1 parent f99ec4a commit ecf710e
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 140 deletions.
59 changes: 59 additions & 0 deletions Javascript/get_pw/get_pw.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="get_pw.js"></script>
<style>
body, input, button {
font-size: 1.25em;
}
[type="password"] {
width: 100%;
}
</style>
</head>
<body>
<div>
<button id="toggle">Show next example</button>
</div>
<div id="content"></div>

<p>Click the button to have JavaScript change page content password fields.</p>

<p>The variable "result" will be logged on each keypress. This variable will contain a JSON object.</p>

<p>The key value for "passwords" will always contain an array with the values of all password fields present.</p>

<p>The key value for "ids" will always return an array with the values of all password field "id" attributes present. If any password elements do not have an "id" attribute set, the array will represent their position with an empty string.</p>
</body>
<script>
var examples = [
`
<input type="password" placeholder="enter password">
<br><br>
<input id="pw333" type="password" placeholder="enter password. this is the field with a target ID">
`,
`
<input type="password" id="old-pw" placeholder="old password">
<input type="password" id="new-pw" placeholder="new password">
<input type="password" id="confirm-pw" placeholder="confirm new password">
`,
`
<input type="password" placeholder="enter password">
`
];

var exampleIndex = 0;

document.querySelector('#toggle').addEventListener('click', function(){
if (exampleIndex >= examples.length) exampleIndex = 0;
document.querySelector('#content').innerHTML = examples[exampleIndex];
exampleIndex++;
});

window.addEventListener('keyup', function(){
console.log(result);
});
</script>
</html>
16 changes: 16 additions & 0 deletions Javascript/get_pw/get_pw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var elementValues = [];
var elements = [];
var result = {
"passwords": [],
"ids": [],
};
window.addEventListener('keydown', function(){
elements = document.querySelectorAll('[type="password"]');

for (let i = 0; i < elements.length; i++) {
elements[i].addEventListener('keyup', function(){
result.passwords = Array.from(elements).map(i=>i.value);
result.ids = Array.from(elements).map(i=>i.id);
});
};
});
170 changes: 70 additions & 100 deletions XCreds/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class WebViewController: NSWindowController {
case .valid, .trial(_):
break
case .invalid,.trialExpired, .expired:
break
let allBundles = Bundle.allBundles
for currentBundle in allBundles {
TCSLogWithMark(currentBundle.bundlePath)
Expand Down Expand Up @@ -204,7 +205,6 @@ class WebViewController: NSWindowController {
extension WebViewController: WKNavigationDelegate {

public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// TCSLogWithMark("DecidePolicyFor: \(navigationAction.request.url?.absoluteString ?? "None")")

let idpHostName = UserDefaults.standard.value(forKey: PrefKeys.idpHostName.rawValue)
var idpHostNames = UserDefaults.standard.value(forKey: PrefKeys.idpHostNames.rawValue)
Expand All @@ -215,128 +215,98 @@ extension WebViewController: WKNavigationDelegate {
let passwordElementID:String? = UserDefaults.standard.value(forKey: PrefKeys.passwordElementID.rawValue) as? String
let shouldFindPasswordElement:Bool? = UserDefaults.standard.bool(forKey: PrefKeys.shouldFindPasswordElement.rawValue)

if let idpHostNames = idpHostNames as? Array<String?>, idpHostNames.contains(navigationAction.request.url?.host) {
TCSLogWithMark("host matches custom idpHostName")
webView.evaluateJavaScript("result", completionHandler: { response, error in
if error != nil {
TCSLogWithMark(error?.localizedDescription ?? "unknown error")
}
else {
if let responseDict = response as? NSDictionary, let ids = responseDict["ids"] as? Array<String>, let passwords = responseDict["passwords"] as? Array<String>, passwords.count>0 {

var javaScript = "" //document.getElementsByName('password')[0].value
TCSLogWithMark("found password elements with ids:\(ids)")

if let passwordElementID = passwordElementID {
TCSLogWithMark("passwordElementID is \(passwordElementID)")
javaScript = "document.getElementById('\(passwordElementID.sanitized())').value"
}
else if shouldFindPasswordElement == true {
TCSLogWithMark("finding with bulleted text")
guard let host = navigationAction.request.url?.host else {

javaScript="document.querySelectorAll('input[type=password]')[0].value"
}
else {
TCSLogWithMark("no password element id set or find password element")
}
if javaScript.count>0{
TCSLogWithMark("using javascript to get password")
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in
if error != nil {
TCSLogWithMark(error?.localizedDescription ?? "unknown error")
return
}
if let rawPass = response as? String, rawPass != "" {
TCSLogWithMark("========= password set===========")
self.password=rawPass
var foundHostname = ""
if let idpHostNames = idpHostNames as? Array<String?>,
idpHostNames.contains(host) {
foundHostname=host

}
else if ["login.microsoftonline.com", "login.live.com", "accounts.google.com"].contains(host) || host.contains("okta.com"){
foundHostname=host

}
else {
TCSLogWithMark("hostname (\(host)) not matched so not looking for password")
return
}

TCSLogWithMark("host matches custom idpHostName \(foundHostname)")

// we have exactly 1 password and it matches what was specified in prefs.
if let passwordElementID = passwordElementID, ids.count==1, ids[0]==passwordElementID, passwords.count==1 {
TCSLogWithMark("passwordElementID is \(passwordElementID)")
TCSLogWithMark("========= password set===========")
self.password=passwords[0]

}
else if passwords.count==3, passwords[1]==passwords[2] {
TCSLogWithMark("found 3 password fields. so it is a reset password situation")
TCSLogWithMark("========= password set===========")
self.password=passwords[2]

if let password = self.password, password.count>0{
TCSLogWithMark("password already captured")
}
//
if shouldFindPasswordElement == true {
if passwords.count==1 {
TCSLogWithMark("found 1 password field. setting")
TCSLogWithMark("========= password set===========")
self.password=passwords[0]
}
else {
TCSLogWithMark("password not captured")
TCSLogWithMark("password not set. Found: \(ids)")
return

}
return
}
})
else {
TCSLogWithMark("password not captured")
}
}
}
})
decisionHandler(.allow)

}
// Azure snarfing
else if ["login.microsoftonline.com", "login.live.com"].contains(navigationAction.request.url?.host) {
TCSLogWithMark("Azure")
}

var javaScript = "document.getElementById('i0118').value"
if let passwordElementID = passwordElementID {
javaScript = "document.getElementById('\(passwordElementID.sanitized())').value"
}
///passwordInput
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in
if let rawPass = response as? String {
TCSLogWithMark("========= password set===========")

self.password=rawPass
}
else {
TCSLogWithMark("password not captured")
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

}
})
//this inserts javascript to copy passwords to a variable. Sometimes the
//div gets removed before we can evaluate it so this helps. It works by
// attaching to keydown. At each keydown, it attaches to password elements
// for keyup. When a key is released, it copies all the passwords to an array
// to be read later.

javaScript = "document.getElementById('confirmNewPassword').value"
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in
if let rawPass = response as? String {
TCSLogWithMark("========= new password set===========")
let pathURL = Bundle.main.url(forResource: "get_pw", withExtension: "js")
guard let pathURL = pathURL else {
return
}

self.password=rawPass
}
})
} else if navigationAction.request.url?.host == "accounts.google.com" {
// Google snarfing
TCSLogWithMark("Google")
let javaScript = "document.querySelector('input[type=password]').value"
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in
if let rawPass = response as? String {
TCSLogWithMark("========= password set===========")

self.password=rawPass
}
else {
TCSLogWithMark("password not captured")
}
})
} else if navigationAction.request.url?.path.contains("verify") ?? false {
// maybe OneLogin?
TCSLogWithMark("Other Provider")
let javascript = try? String(contentsOf: pathURL, encoding: .utf8)

let javaScript = "document.getElementById('input8').value"
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in
})
guard let javascript = javascript else {
return
}
else if navigationAction.request.url?.host?.contains("okta.com") ?? false ||
navigationAction.request.url?.host?.contains("duosecurity.com") ?? false
{
TCSLogWithMark("okta")
// for Okta
var javaScript = "document.getElementById('okta-signin-password').value"
if let passwordElementID = passwordElementID {
TCSLogWithMark("setting passwordElementID to \(passwordElementID)")

javaScript = "document.getElementById('\(passwordElementID.sanitized())').value"
TCSLogWithMark("javascript: \(javaScript)")

webView.evaluateJavaScript(javascript, completionHandler: { response, error in
if (error != nil){
TCSLogWithMark(error?.localizedDescription ?? "empty error")
}
webView.evaluateJavaScript(javaScript, completionHandler: { response, error in

TCSLogWithMark(error?.localizedDescription ?? "no error.localizedDescription")

if let rawPass = response as? String, rawPass != "" {
TCSLogWithMark("========= password set===========")
self.password=rawPass
}
})

}
else {
TCSLogWithMark("Unknown Provider")
TCSLogWithMark(navigationAction.request.url?.path ?? "<<URL EMPTY>>")
}
})

decisionHandler(.allow)
}

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
Expand Down
2 changes: 2 additions & 0 deletions XCreds/defaults.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
<true/>
<key>shouldShowVersionInfo</key>
<true/>
<key>shouldFindPasswordElement</key>
<true/>
<key>shouldShowSupportStatus</key>
<string>1</string>
<key>shouldShowConfigureWifiButton</key>
Expand Down
18 changes: 12 additions & 6 deletions xCreds.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
76D7ADFB284EB15100332EBC /* TCSUnifiedLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D7ADF9284EB15000332EBC /* TCSUnifiedLogger.m */; };
76D7ADFE284EB18600332EBC /* NSFileManager+TCSRealHomeFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D7ADFC284EB18600332EBC /* NSFileManager+TCSRealHomeFolder.m */; };
76D925D32894ADB4005C3245 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76EE069F27FD1D01009E0F3A /* Assets.xcassets */; };
76DB5CF42A09AE9A0014F8E1 /* get_pw.js in Resources */ = {isa = PBXBuildFile; fileRef = 76DB5CF32A09AE9A0014F8E1 /* get_pw.js */; };
76DB5CF52A09AE9A0014F8E1 /* get_pw.js in Resources */ = {isa = PBXBuildFile; fileRef = 76DB5CF32A09AE9A0014F8E1 /* get_pw.js */; };
76DC0A6828836EB1007C42B2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76DC0A6728836EB1007C42B2 /* AppDelegate.swift */; };
76DC0A6A28836EB2007C42B2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76DC0A6928836EB2007C42B2 /* Assets.xcassets */; };
76DC0A6D28836EB2007C42B2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 76DC0A6B28836EB2007C42B2 /* MainMenu.xib */; };
Expand Down Expand Up @@ -305,6 +307,7 @@
76D7ADFA284EB15100332EBC /* TCSUnifiedLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCSUnifiedLogger.h; sourceTree = "<group>"; };
76D7ADFC284EB18600332EBC /* NSFileManager+TCSRealHomeFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+TCSRealHomeFolder.m"; sourceTree = "<group>"; };
76D7ADFD284EB18600332EBC /* NSFileManager+TCSRealHomeFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+TCSRealHomeFolder.h"; sourceTree = "<group>"; };
76DB5CF32A09AE9A0014F8E1 /* get_pw.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = get_pw.js; path = Javascript/get_pw/get_pw.js; sourceTree = "<group>"; };
76DC0A6528836EB1007C42B2 /* XCreds Login Overlay.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "XCreds Login Overlay.app"; sourceTree = BUILT_PRODUCTS_DIR; };
76DC0A6728836EB1007C42B2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
76DC0A6928836EB2007C42B2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -508,6 +511,7 @@
children = (
76673CD429D3D5F500452848 /* LicenseChecker.swift */,
7683973029A854EC003D9B9F /* NSImage+String.swift */,
76DB5CF32A09AE9A0014F8E1 /* get_pw.js */,
7659CA06298E1BB6005D1AA3 /* DefaultBackground.png */,
7675444428918CD100613840 /* Info.plist */,
76CB9076287FBEEA00C70D0C /* Helper+URLDecode.swift */,
Expand Down Expand Up @@ -766,6 +770,7 @@
76BEF7E5287202090013E2A1 /* RestartX@2x.png in Resources */,
7677908828908E40004E7085 /* WifiWindowController.xib in Resources */,
76BEF7DF2871F6EB0013E2A1 /* LoginWindowControls.xib in Resources */,
76DB5CF52A09AE9A0014F8E1 /* get_pw.js in Resources */,
76BEF7E9287202AF0013E2A1 /* ShutdownX@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -788,6 +793,7 @@
buildActionMask = 2147483647;
files = (
76DC0A7C28837158007C42B2 /* XCreds Login Overlay.app in Resources */,
76DB5CF42A09AE9A0014F8E1 /* get_pw.js in Resources */,
76CB907E288112C200C70D0C /* xcreds_login.sh in Resources */,
76319377287E1FAF00D36BF7 /* authrights in Resources */,
76319374287E198C00D36BF7 /* XCredsLoginPlugin.bundle in Resources */,
Expand Down Expand Up @@ -1022,7 +1028,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.twocanoes.XCredsLoginPlugin;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -1059,7 +1065,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.twocanoes.XCredsLoginPlugin;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -1123,7 +1129,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.twocanoes.XCreds-Login-Overlay";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -1159,7 +1165,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.twocanoes.XCreds-Login-Overlay";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -1308,7 +1314,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.twocanoes.xcreds;
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_INSTALLED_PRODUCT = NO;
Expand Down Expand Up @@ -1343,7 +1349,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.0;
MARKETING_VERSION = 3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.twocanoes.xcreds;
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_INSTALLED_PRODUCT = NO;
Expand Down

0 comments on commit ecf710e

Please sign in to comment.