From cce9bd036b7acf98360e73f2600c244093101333 Mon Sep 17 00:00:00 2001 From: pierre-loup-tristant-sonarsource Date: Wed, 18 Jun 2025 13:31:35 +0000 Subject: [PATCH] Create rule S7612 --- rules/S7612/common/compliant.adoc | 3 + rules/S7612/common/resources/docs.adoc | 3 + rules/S7612/common/resources/standards.adoc | 7 ++ rules/S7612/highlighting.adoc | 7 ++ rules/S7612/impact.adoc | 7 ++ rules/S7612/java/how-to-fix-it/android.adoc | 71 ++++++++++++++++++ rules/S7612/java/metadata.json | 2 + rules/S7612/java/rule.adoc | 29 ++++++++ rules/S7612/kotlin/how-to-fix-it/android.adoc | 73 +++++++++++++++++++ rules/S7612/kotlin/metadata.json | 2 + rules/S7612/kotlin/rule.adoc | 29 ++++++++ rules/S7612/message.adoc | 3 + rules/S7612/metadata.json | 56 ++++++++++++++ rules/S7612/rationale.adoc | 1 + rules/S7612/summary.adoc | 2 + 15 files changed, 295 insertions(+) create mode 100644 rules/S7612/common/compliant.adoc create mode 100644 rules/S7612/common/resources/docs.adoc create mode 100644 rules/S7612/common/resources/standards.adoc create mode 100644 rules/S7612/highlighting.adoc create mode 100644 rules/S7612/impact.adoc create mode 100644 rules/S7612/java/how-to-fix-it/android.adoc create mode 100644 rules/S7612/java/metadata.json create mode 100644 rules/S7612/java/rule.adoc create mode 100644 rules/S7612/kotlin/how-to-fix-it/android.adoc create mode 100644 rules/S7612/kotlin/metadata.json create mode 100644 rules/S7612/kotlin/rule.adoc create mode 100644 rules/S7612/message.adoc create mode 100644 rules/S7612/metadata.json create mode 100644 rules/S7612/rationale.adoc create mode 100644 rules/S7612/summary.adoc diff --git a/rules/S7612/common/compliant.adoc b/rules/S7612/common/compliant.adoc new file mode 100644 index 00000000000..1fe447cfebc --- /dev/null +++ b/rules/S7612/common/compliant.adoc @@ -0,0 +1,3 @@ +Applications storing passwords locally often do so because they want users to stay logged in. +To keep users logged in, use or implement session management on the server side. +If not possible or applicable, use the Android Credential Manager \ No newline at end of file diff --git a/rules/S7612/common/resources/docs.adoc b/rules/S7612/common/resources/docs.adoc new file mode 100644 index 00000000000..5be19801524 --- /dev/null +++ b/rules/S7612/common/resources/docs.adoc @@ -0,0 +1,3 @@ +=== Documentation + +* Android Documentation - https://developer.android.com/identity/sign-in/credential-manager[Sign in your user with Credential Manager] diff --git a/rules/S7612/common/resources/standards.adoc b/rules/S7612/common/resources/standards.adoc new file mode 100644 index 00000000000..bf399029fad --- /dev/null +++ b/rules/S7612/common/resources/standards.adoc @@ -0,0 +1,7 @@ +=== Standards + +* OWASP - https://owasp.org/Top10/A04_2021-Insecure_Design/[Top 10 2021 Category A4 - Insecure Design] +* OWASP - https://owasp.org/www-project-top-ten/2017/A3_2017-Sensitive_Data_Exposure[Top 10 2017 Category A3 - Sensitive Data Exposure] +* OWASP - https://owasp.org/www-project-mobile-top-10/2023-risks/m9-insecure-data-storage.html[Mobile Top 10 2024 Category M9 - Insecure Data Storage] +* CWE - https://cwe.mitre.org/data/definitions/256[CWE-256 - Plaintext Storage of a Password] +* STIG Viewer - https://stigviewer.com/stigs/application_security_and_development/2024-12-06/finding/V-222602[Application Security and Development: V-222542] - The application must only store cryptographic representations of passwords. diff --git a/rules/S7612/highlighting.adoc b/rules/S7612/highlighting.adoc new file mode 100644 index 00000000000..cff89a6dc1b --- /dev/null +++ b/rules/S7612/highlighting.adoc @@ -0,0 +1,7 @@ +=== Highlighting + +"[varname]" is tainted (assignments and parameters) + +this argument is tainted (method invocations) + +the returned value is tainted (returns & method invocations results) \ No newline at end of file diff --git a/rules/S7612/impact.adoc b/rules/S7612/impact.adoc new file mode 100644 index 00000000000..35e3653d7ec --- /dev/null +++ b/rules/S7612/impact.adoc @@ -0,0 +1,7 @@ +=== What is the potential impact? + +==== Account Compromise +The most direct problem is that an attacker can take over the user's account in your application. By reading the stored password from the device, the attacker can log in as the user. Once inside, they could potentially view private information, send messages while impersonating the user, or access other features of the account. + +==== The Danger of Password Reuse +Using the same password for multiple websites and applications is a common behavior. If attackers find a password from one application, they will often try that same password on other, more valuable services, such as email, banking, or social media accounts. This is a technique known as credential stuffing. A security weakness in one application can therefore lead to a much broader compromise of a user's digital life. diff --git a/rules/S7612/java/how-to-fix-it/android.adoc b/rules/S7612/java/how-to-fix-it/android.adoc new file mode 100644 index 00000000000..dab86d34377 --- /dev/null +++ b/rules/S7612/java/how-to-fix-it/android.adoc @@ -0,0 +1,71 @@ +== How to fix it in Android + +=== Code examples + +==== Noncompliant code example + +[source,java,diff-id=11,diff-type=noncompliant] +---- +public class ExampleActivity extends AppCompatActivity { + + private EditText passwordEditText; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_xml_layout); + + SharedPreferences sharedPreferences = getSharedPreferences("example", Context.MODE_PRIVATE); + + passwordEditText = findViewById(R.id.passwordEditText); + Button savePasswordButton = findViewById(R.id.savePasswordButton); + + savePasswordButton.setOnClickListener(v -> { + String password = passwordEditText.getText().toString(); + if (!password.isEmpty()) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("Password", password); + editor.apply(); + } + }); + } +} +---- + +==== Compliant solution + +include::../../common/compliant.adoc[] + +[source,java,diff-id=11,diff-type=compliant] +---- +public class ExampleActivity extends AppCompatActivity { + + private EditText passwordEditText; + private CredentialManagerCallback callback; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_xml_layout); + + SharedPreferences sharedPreferences = getSharedPreferences("example", Context.MODE_PRIVATE); + + passwordEditText = findViewById(R.id.passwordEditText); + Button savePasswordButton = findViewById(R.id.savePasswordButton); + + savePasswordButton.setOnClickListener(v -> { + String password = passwordEditText.getText().toString(); + if (!password.isEmpty()) { + CredentialManager credentialManager = CredentialManager.create(this); + CreatePasswordRequest request = new CreatePasswordRequest( + "example", + password + ); + credentialManager.createCredentialAsync (this, request,null, Runnable::run, callback); + } + }); + } +} +---- \ No newline at end of file diff --git a/rules/S7612/java/metadata.json b/rules/S7612/java/metadata.json new file mode 100644 index 00000000000..7a73a41bfdf --- /dev/null +++ b/rules/S7612/java/metadata.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/rules/S7612/java/rule.adoc b/rules/S7612/java/rule.adoc new file mode 100644 index 00000000000..a0a1c2fc77b --- /dev/null +++ b/rules/S7612/java/rule.adoc @@ -0,0 +1,29 @@ +include::../summary.adoc[] + +== Why is this an issue? + +include::../rationale.adoc[] + +include::../impact.adoc[] + +// How to fix it section + +include::how-to-fix-it/android.adoc[] + +== Resources + +include::../common/resources/docs.adoc[] + +include::../common/resources/standards.adoc[] + +ifdef::env-github,rspecator-view[] + +''' +== Implementation Specification +(visible only on this page) + +include::../message.adoc[] + +include::../highlighting.adoc[] + +endif::env-github,rspecator-view[] \ No newline at end of file diff --git a/rules/S7612/kotlin/how-to-fix-it/android.adoc b/rules/S7612/kotlin/how-to-fix-it/android.adoc new file mode 100644 index 00000000000..8b91092db74 --- /dev/null +++ b/rules/S7612/kotlin/how-to-fix-it/android.adoc @@ -0,0 +1,73 @@ +== How to fix it in Android + +=== Code examples + +==== Noncompliant code example + +[source,kotlin,diff-id=21,diff-type=noncompliant] +---- +private val Context.dataStore: DataStore by preferencesDataStore(name = "user_prefs_datastore") + +@Composable +fun PasswordInputScreen(modifier: Modifier = Modifier, onSavePassword: (String) -> Unit) { + var password by remember { mutableStateOf("") } + val context = LocalContext.current + val coroutineScope = rememberCoroutineScope() // Add this line + + Column() { + TextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + visualTransformation = PasswordVisualTransformation(), + ) + Button(onClick = { + coroutineScope.launch { // Launch a coroutine + context.dataStore.edit { preferences -> + preferences[stringPreferencesKey("saved_password")] = password + } + } + }) { + Text("Save Password") + } + } +} +---- + +==== Compliant solution + +include::../../common/compliant.adoc[] + +[source,kotlin,diff-id=21,diff-type=compliant] +---- +@Composable +fun PasswordInputScreen(modifier: Modifier = Modifier, onSavePassword: (String) -> Unit) { + var password by remember { mutableStateOf("") } + val context = LocalContext.current + val coroutineScope = rememberCoroutineScope() // Add this line + + Column() { + TextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + visualTransformation = PasswordVisualTransformation(), + ) + Button(onClick = { + coroutineScope.launch { // Launch a coroutine + val credentialManager = CredentialManager.create(context) + val request = CreatePasswordRequest( + id = "example", + password = password + ) + credentialManager.createCredential( + request = request, + context = context + ) + } + }) { + Text("Save Password") + } + } +} +---- \ No newline at end of file diff --git a/rules/S7612/kotlin/metadata.json b/rules/S7612/kotlin/metadata.json new file mode 100644 index 00000000000..7a73a41bfdf --- /dev/null +++ b/rules/S7612/kotlin/metadata.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/rules/S7612/kotlin/rule.adoc b/rules/S7612/kotlin/rule.adoc new file mode 100644 index 00000000000..a0a1c2fc77b --- /dev/null +++ b/rules/S7612/kotlin/rule.adoc @@ -0,0 +1,29 @@ +include::../summary.adoc[] + +== Why is this an issue? + +include::../rationale.adoc[] + +include::../impact.adoc[] + +// How to fix it section + +include::how-to-fix-it/android.adoc[] + +== Resources + +include::../common/resources/docs.adoc[] + +include::../common/resources/standards.adoc[] + +ifdef::env-github,rspecator-view[] + +''' +== Implementation Specification +(visible only on this page) + +include::../message.adoc[] + +include::../highlighting.adoc[] + +endif::env-github,rspecator-view[] \ No newline at end of file diff --git a/rules/S7612/message.adoc b/rules/S7612/message.adoc new file mode 100644 index 00000000000..8147c0f2ac0 --- /dev/null +++ b/rules/S7612/message.adoc @@ -0,0 +1,3 @@ +=== Message + +Change this code to not user's password on the device. \ No newline at end of file diff --git a/rules/S7612/metadata.json b/rules/S7612/metadata.json new file mode 100644 index 00000000000..57df14c25a6 --- /dev/null +++ b/rules/S7612/metadata.json @@ -0,0 +1,56 @@ +{ + "title": "Passwords should not be stored on device", + "type": "VULNERABILITY", + "code": { + "impacts": { + "SECURITY": "HIGH" + }, + "attribute": "COMPLETE" + }, + "status": "ready", + "remediation": { + "func": "Constant/Issue", + "constantCost": "1d" + }, + "tags": [ + ], + "extra": { + }, + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-7612", + "sqKey": "S7612", + "scope": "All", + "securityStandards": { + "CWE": [ + 256 + ], + "OWASP": [ + "A3" + ], + "OWASP Top 10 2021": [ + "A4" + ], + "OWASP Mobile Top 10 2024": [ + "M9" + ], + "PCI DSS 3.2": [ + "6.5.3" + ], + "PCI DSS 4.0": [ + "6.2.4" + ], + "ASVS 4.0": [ + "2.10.3", + "2.4.1", + "2.4.2", + "2.4.3", + "2.4.4", + "2.4.5" + ], + "STIG ASD_V5R3": [ + "V-222542" + ] + }, + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "unknown" +} diff --git a/rules/S7612/rationale.adoc b/rules/S7612/rationale.adoc new file mode 100644 index 00000000000..f49028e3d40 --- /dev/null +++ b/rules/S7612/rationale.adoc @@ -0,0 +1 @@ +When an application stores a password without encryption, it creates a security risk for the user. Often, the password is not directly accessible by a third-parties thanks to OS and application level protections. Yet password leakage can happen if the device is lost or stolen, or if an attacker compromises the application. diff --git a/rules/S7612/summary.adoc b/rules/S7612/summary.adoc new file mode 100644 index 00000000000..ab114ec49cf --- /dev/null +++ b/rules/S7612/summary.adoc @@ -0,0 +1,2 @@ +This vulnerability occurs when an application saves password, in plaintext on the device's storage. +Password is a risk of being leaked if the device or the application itself is compromised. \ No newline at end of file