importSwiftUIimportUIKit//SUMMARY:ThissampledemonstratestheuseofbothsecureandinsecuretextinputfieldsinaniOSapp.Itincludesaloginalertwithausernamefield(insecure),apasswordfield(insecure),andanOTPfield(secure).Additionally,itpresentsaSwiftUIviewforenteringasecondOTP,whichisalsosecure.Thetestchecksforthepresenceoftextinputfieldsandwhethertheyareconfiguredtomasksensitiveinformation.structMastgTest{@inline(never)@_optimize(none)publicstaticfuncmastgTest(completion:@escaping(String)->Void){DispatchQueue.main.async{letalert=UIAlertController(title:"Login",message:"Please enter your credentials.",preferredStyle:.alert)alert.addTextField{tfintf.placeholder="Username"tf.autocorrectionType=.notf.accessibilityIdentifier="username_field"}//FAIL:[MASTG-TEST-0x57]ThepasswordfieldhasisSecureTextEntrysettofalse.alert.addTextField{tfintf.placeholder="Password"tf.isSecureTextEntry=falsetf.accessibilityIdentifier="password_field"}//PASS:[MASTG-TEST-0x57]TheOTP1fieldhasisSecureTextEntrysettotrue.alert.addTextField{tfintf.placeholder="OTP 1"tf.isSecureTextEntry=truetf.keyboardType=.numberPadtf.accessibilityIdentifier="otp_1_field"}alert.addAction(UIAlertAction(title:"Next",style:.default,handler:{_inletusername=alert.textFields?[0].text??""letotp1=alert.textFields?[2].text??""letbaseMessage="Logged in as: \(username), OTP 1: \(otp1)"letotpView=OTPView{otp2inifletpresenter=topViewController(){presenter.dismiss(animated:true){completion("\(baseMessage), OTP 2: \(otp2)")}}else{completion("\(baseMessage), OTP 2: \(otp2)")}}lethosting=UIHostingController(rootView:otpView)ifletpresenter=topViewController(){presenter.present(hosting,animated:true)}}))ifletpresenter=topViewController(){presenter.present(alert,animated:true)}else{completion("Failed to present alert.")}}}structOTPView:View{@Stateprivatevarotp2:String=""varonSubmit:(String)->Voidvarbody:someView{VStack(spacing:20){Text("Enter OTP 2")SecureField("OTP 2",text:$otp2).keyboardType(.numberPad).textFieldStyle(RoundedBorderTextFieldStyle()).accessibilityIdentifier("otp_2_field").padding()Button("Submit"){onSubmit(otp2)}}.padding()}}privatestaticfunctopViewController(base:UIViewController?={letscenes=UIApplication.shared.connectedScenes.compactMap{$0as?UIWindowScene}letkeyWindow=scenes.flatMap{$0.windows}.first{$0.isKeyWindow}returnkeyWindow?.rootViewController}())->UIViewController?{ifletnav=baseas?UINavigationController{returntopViewController(base:nav.visibleViewController)}iflettab=baseas?UITabBarController{returntopViewController(base:tab.selectedViewController)}ifletpresented=base?.presentedViewController{returntopViewController(base:presented)}returnbase}}
The "Password" field is not masked, and is therefore visible to bystanders (shoulder surfing) or potentially captured in screenshots and screen recordings.
The "OTP 1" and "OTP 2" fields use text input fields that are configured to mask the inputted data.
e asm.bytes=false
e scr.color=false
e scr.interactive=false
e asm.var=false
e bin.relocs.apply=true
?e List all references to "UITextField", "setSecureTextEntry" and "SecureField":
f~UITextField,setSecureTextEntry,SecureField
?e
?e Cross-references to the "setSecureTextEntry:" selector:
axt @ 0x000201c0
?e
?e Cross-references to the "SecureField" initializer:
axt @ 0x000040a4
?e
?e Disassembly around the password field setup (isSecureTextEntry = false, FAIL):
pd--10 @ 0x1a30
?e
?e Disassembly around the OTP 1 field setup (isSecureTextEntry = true, PASS):
pd--10 @ 0x1b40
?e
?e Disassembly around the SecureField (OTP 2) setup (PASS):
pd 8 @ 0x3c50
The test case fails because the password field calls setSecureTextEntry: with argument false, leaving the sensitive input unmasked.
Interpreting the Disassembly:
Although MastgTest.swift is written in Swift, it interacts with UIKit (an Objective-C framework). The compiler translates these interactions into calls to the objc_msgSend function. We analyze the arguments passed to this function using the ARM64 calling convention:
x0 register: holds self (the instance of the UITextField).
x1 register: holds the selector (the method name).
x2 register: holds the argument passed to that method.
Password Field (FAIL):
The setSecureTextEntry: call at 0x00001a24 corresponds to the password field. The disassembly shows:
At 0x00001a28, mov w8, 0 sets the value to 0. At 0x00001a2c, and w2, w8, 1 prepares the boolean argument, resulting in w2 = 0 (false).
This means isSecureTextEntry is set to false for the password field, causing the password to be displayed in plain text instead of being masked with bullet characters.
OTP 1 Field (PASS):
The setSecureTextEntry: call at 0x00001b38 corresponds to the OTP 1 field. The disassembly shows:
At 0x00001ae0, mov w8, 1 stores the value 1 (true) into a local variable. At 0x00001b2c, the stored value is reloaded. At 0x00001b3c, and w2, w8, 1 results in w2 = 1 (true).
This means isSecureTextEntry is true for the OTP 1 field, so the input is correctly masked.
OTP 2 Field (PASS):
The call at 0x00003c60 invokes the SecureField initializer directly (bl sym.SwiftUI.SecureField...). SwiftUI's SecureField always masks input by design — there is no isSecureTextEntry setter involved.