Skip to content

security: protect sensitive user credentials in RAM via XOR mask. - #2942

Closed
dev12124 wants to merge 4 commits into
Acode-Foundation:mainfrom
dev12124:main
Closed

dev12124 wants to merge 4 commits into
Acode-Foundation:mainfrom
dev12124:main

Conversation

@dev12124

Copy link
Copy Markdown

This PR increment a mask XOR (using 0x5A in the Variable MASK_KEY), and apply a Mask in Sensitive User Credentials (e.g: Email, WebSite, GitHub etc).

I intend to bring further security enhancements in the future, such as implementing a dynamic mask key instead of a fixed value (which currently uses 0x5A).

I´m added a Mask using 0x5A for have a mask in the Critic Variables. e.g:
I´m create a Variable using let named "secret_key" with attribute "secret_password". This Variable is in the RAM, and a Malware-Plugin installed have access this Variable and modify. And, e.g: The Variable loggedInUser have a Critic Properties ("email", "github", "website") and this Malware-Plugin access and view this Variable.
…ser data in RAM

This PR introduces a security enhancement to mitigate credential scraping vulnerabilities in memory (RAM).

Plain-text variables containing sensitive user info (such as emails, GitHub tokens, and websites) are vulnerable to inspection or tampering by potentially malicious third-party plugins running within the same environment.

Changes implemented:
- Added a security module (`security.js`) featuring simple and lightweight dynamic masking using a fixed XOR key (0x5A).
- Integrated `secureUserObject` and `getDecryptedUser` inside `auth.js` to ensure data remains obfuscated while stored in RAM/localStorage, preventing crashes during UI rendering.
@greptile-apps

greptile-apps Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 3/5

[Medium risk] Adds XOR masking to user credentials in memory.

The PR is not safe to merge until cached users are decrypted on fallback and Unicode strings survive masking.

Findings

  1. P1 Cached user remains masked ▶
  2. P1 Unicode characters are corrupted ▶
  3. P2 Security Fixed mask cannot protect credentials ▶

Summary

The PR masks selected user string fields in the in-memory auth object, stores the masked object in the local cache, and decrypts it on normal getter paths.

  • The cache-fallback path omits decryption, and the mask corrupts non-BMP Unicode characters.
  • The fixed mask does not isolate user data from plugins running in the app document.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["/login user"] --> B["Mask string fields"]
  B --> C["In-memory user"]
  B --> D["localStorage cache"]
  C --> E["Decrypt before return"]
  D --> F["Fetch-failure fallback"]
  F --> G["Return masked arrays without decryption"]
Loading

Reviews (1) · Last reviewed commit: "security: protect sensitive user credent..."

Comment thread src/lib/auth.js
return loggedInUser;
const rawuser = await res.json();
loggedInUser = secureUserObject(rawuser);
localStorage.setItem(CACHE_USER_KEY, JSON.stringify(loggedInUser));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Cached user remains masked

When /login fails after a user has been cached, the fallback returns the stored user without decrypting its string fields. Those fields are now arrays, so the sidebar can throw when it calls name.split(" ") instead of displaying the cached user. Decrypt the parsed cache before returning it.

Comment thread src/security/security.js
export function maskCredential(secretString) {
if (!secretString) return [];

return Array.from(secretString).map(char => char.charCodeAt(0) ^ MASK_KEY);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unicode characters are corrupted

For a name containing an emoji or another character outside the basic Unicode range, Array.from keeps the character together but charCodeAt(0) saves only its first UTF-16 unit. Unmasking cannot restore the missing unit, so the user's name is permanently changed. Mask each UTF-16 unit so fromCharCode can reconstruct the original string.

Suggested change
return Array.from(secretString).map(char => char.charCodeAt(0) ^ MASK_KEY);
return secretString.split("").map(char => char.charCodeAt(0) ^ MASK_KEY);

Comment thread src/security/security.js
* In JavaScript, plain text variables reside unprotected in the RAM,
* making them vulnerable to access or modification by malicious plugins.
*/
const MASK_KEY = 0x5A;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Fixed mask cannot protect credentials

This fixed XOR key does not protect the cached user fields from a malicious plugin. Plugins run in the app document, so one can read the masked user from the same localStorage and reverse the key. The practical cost is that this adds masking without providing the claimed protection; that requires limiting plugin access to the data or isolating plugin execution.

How this was verified: Plugin scripts execute in the app document and can read the same localStorage entry that holds user fields encoded with the fixed XOR key.

@RohitKushvaha01

RohitKushvaha01 commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Again, XOR does not protect anything here, and the user details you are trying to protect are not that sensitive

@dev12124

Copy link
Copy Markdown
Author

With all due respect, dismissing credential obfuscation in memory as "not protecting anything" overlooks fundamental principles of defensive programming and modern application security. 

While a XOR mask with a static key is a lightweight mechanism, its primary objective in this context is to mitigate basic Heap Inspection / RAM Dump exploitation. In environments like Cordova/WebView where third-party plugins share the same execution context, storing sensitive customer variables (such as raw GitHub tokens and emails) in plain text makes credential scraping trivial for any malicious or compromised package. 

Implementing an obfuscation layer enforces a Defense in Depth strategy. It ensures that even if an attacker or a malicious script gains read access to the application's memory pool, they cannot capture valid API tokens through a simple automated plaintext string scan. 

We are protecting the user's entry points and automated access keys—data that should never reside unprotected in memory.

@dev12124

Copy link
Copy Markdown
Author

I'm creating a processor named "Xenon" in SystemVerilog. And you? An application for Android cell phones? Yes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants