Skip to content

Conversation

@saicharan-balina
Copy link

@saicharan-balina saicharan-balina commented Jun 16, 2025

This PR addresses Issue #2 by improving the security and stability of the extension's API usage.

🔐 API Key Security Enhancements
Replaced localStorage with sessionStorage for sensitive API key storage.

Added basic obfuscation using btoa() / atob().

Implemented "Session Only" mode to store API key in memory instead of any storage.

📉 Throttling and Request Batching
Introduced throttling: 1 API call per 500ms using a request queue.

Added exponential backoff logic for HTTP 429 Too Many Requests errors.

Batched large full-page translation texts into paragraph-sized requests to minimize load.

📊 API Usage Feedback
API call count is now tracked using chrome.storage.local.

UI updated to display: "API Calls Used: N" in the popup.

✅ Benefits
🔐 Improved user data security

📉 Prevents API rate limit issues

📊 Transparent usage tracking

🚀 Better extension performance and UX

Tested and verified functionality. Let me know if you'd like any changes — happy to iterate!

Summary by CodeRabbit

  • New Features

    • Added secure, encrypted API key storage with support for session-only or persistent options.
    • Introduced API usage statistics and a reset counter in the popup UI.
    • Implemented batch translation with real-time progress indicators for faster page translations.
    • Added copy-to-clipboard buttons for translation and explanation results.
    • Enhanced API key validation and setup flow with improved feedback and error handling.
  • Improvements

    • Refined popup and welcome UI with new styling, spinners, and better accessibility.
    • Improved reliability of API requests with built-in throttling, retries, and status tracking.
    • Enhanced dark mode and visual feedback for all popup elements.
  • Bug Fixes

    • Fixed CSP issues by delegating all API key and translation operations to the background script.

@coderabbitai
Copy link

coderabbitai bot commented Jun 16, 2025

Walkthrough

This update introduces two new utility classes for secure API key storage and API request management, implements encrypted and session-only API key handling, adds throttling, retries, and batch processing for API calls, and refactors UI components for improved feedback, statistics, and progress indicators. Multiple files were updated to support these features.

Changes

File(s) Change Summary
background.js, utils/securityHelper.js, utils/apiRequestManager.js Added SecurityHelper and ApiRequestManager classes for encrypted API key storage and managed API requests with throttling, retries, and batching. Integrated these into background logic and API handling.
content.js Refactored paragraph translation to use batch processing with progress UI and messaging, replacing sequential translation logic.
manifest.json Removed "type": "module" from background, reformatted content_scripts, and added web_accessible_resources for new utility scripts.
popup/popup.js, popup/popup.html Refactored popup to use background messaging for API key management, added session-only option, usage stats, reset button, improved UI feedback, and delegated all API actions to background.
popup/popup.css Enhanced and unified UI styling, added transitions, spinners, progress bars, and improved accessibility and feedback for all popup components.
popup/welcome.html, popup/welcome.js Updated API key setup with session-only option, improved validation flow, loading states, and user feedback, with new inline styles and messaging.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Popup
    participant Background
    participant SecurityHelper
    participant ApiRequestManager

    User->>Popup: Enter API key and settings
    Popup->>Background: Save/check API key (with sessionOnly flag)
    Background->>SecurityHelper: storeApiKey(apiKey, sessionOnly)
    SecurityHelper-->>Background: (encrypted, stored)
    Background-->>Popup: Confirmation/status

    User->>Popup: Request translation/explanation
    Popup->>Background: API request (translate/explain)
    Background->>ApiRequestManager: addRequest(requestFn)
    ApiRequestManager->>Background: (throttled, retried if needed)
    Background-->>Popup: Result or error

    User->>Popup: Translate Page
    Popup->>Content Script: Start batch translation
    Content Script->>Background: Batch translation request
    Background->>ApiRequestManager: processBatch(items, ...)
    ApiRequestManager->>Background: Process batches, send progress
    Background->>Content Script: Progress updates
    Content Script->>Popup: Show progress UI
Loading

Possibly related issues

Poem

A rabbit hopped through code so bright,
Encrypting keys and storing right.
With batches, stats, and spinners spun,
Throttled calls—no need to run!
Popups gleam in dark and light,
Secure and swift, the code takes flight.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (10)
utils/securityHelper.js (2)

24-30: Static context: prefer explicit class name instead of this

Biome warns that this inside a static method can be confusing.
Small diff keeps linters quiet:

-const encryptedKey = this.encryptApiKey(apiKey);
+const encryptedKey = SecurityHelper.encryptApiKey(apiKey);
 ...
-return this.decryptApiKey(encryptedKey);
+return SecurityHelper.decryptApiKey(encryptedKey);

Also applies to: 63-69


1-3: Static-only class → convert to module of pure functions

The class only groups statics; no instances are ever created. A simple ES module:

export async function storeApiKey(...) { ... }
export async function getApiKey()  { ... }

reduces boilerplate and sidesteps the noStaticOnlyClass lint error.

Also applies to: 87-92

manifest.json (2)

22-24: JSON formatting – compressed lines hinder diff reviews

The "host_permissions" closing bracket and the "background" object are on the same line. While syntactically valid, it hurts readability and increases merge-conflict odds. Split onto separate lines.


40-44: Exposing internal utilities as web-accessible resources leaks attack surface

Nothing outside the extension should load utils/securityHelper.js or utils/apiRequestManager.js.
Remove them from web_accessible_resources unless you have a concrete need; otherwise any page can fetch and inspect the code (or future secrets).

popup/popup.html (1)

21-23: Accessibility: toggle button label does not update

Show never switches to Hide, so screen-reader users can’t know the current state.
After toggling type, update the button text (ShowHide) and add aria-pressed.

popup/welcome.js (1)

133-149: Use optional-chaining when reading the background response

response && response.success can be shortened to response?.success. Same for the error path.
Not critical, but it silences the Biome hint and tightens the code.

popup/popup.css (1)

238-352: Consolidate duplicated .success-message / .error-message rules

The selectors are defined twice (lines 238-261 and again 292-352). This causes needless bloat and makes future edits error-prone. Merge them into one block.

popup/welcome.html (3)

6-54: Extract inline styles into external CSS
The large <style> block bloats the HTML and makes maintenance harder. Move these rules into popup.css to keep the markup lean, promote reuse, and improve load performance.


7-20: Enhance spinner accessibility and compatibility
The .loading spinner is purely visual. Add aria-hidden="true" or provide a visually hidden text alternative when showing the spinner, and consider vendor-prefixed @-webkit-keyframes for broader browser support.


67-71: Improve semantic grouping of the session option
Wrap the checkbox and its label in a <fieldset>/<legend> or add ARIA roles to better convey grouping to assistive technologies.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f37fb5b and d601c78.

📒 Files selected for processing (10)
  • background.js (6 hunks)
  • content.js (1 hunks)
  • manifest.json (2 hunks)
  • popup/popup.css (7 hunks)
  • popup/popup.html (2 hunks)
  • popup/popup.js (1 hunks)
  • popup/welcome.html (1 hunks)
  • popup/welcome.js (1 hunks)
  • utils/apiRequestManager.js (1 hunks)
  • utils/securityHelper.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
utils/securityHelper.js (2)
background.js (4)
  • apiKey (578-578)
  • apiKey (637-637)
  • chrome (584-584)
  • chrome (643-643)
content.js (1)
  • chrome (25-25)
popup/popup.js (2)
content.js (3)
  • originalText (174-174)
  • chrome (25-25)
  • response (177-180)
background.js (8)
  • originalText (788-788)
  • originalText (891-891)
  • message (735-735)
  • chrome (584-584)
  • chrome (643-643)
  • stats (354-357)
  • apiKey (578-578)
  • apiKey (637-637)
background.js (2)
content.js (8)
  • chrome (25-25)
  • batchSize (98-98)
  • i (75-75)
  • i (101-101)
  • batch (102-102)
  • progress (126-126)
  • response (177-180)
  • originalText (174-174)
popup/welcome.js (1)
  • errorMessage (8-8)
🪛 Biome (1.9.4)
utils/securityHelper.js

[error] 1-87: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 63-63: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 68-68: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

popup/welcome.js

[error] 133-133: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 147-147: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

popup/popup.js

[error] 365-365: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

background.js

[error] 2-96: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 27-27: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 59-59: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 716-716: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 776-776: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 879-879: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 985-985: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

content.js

[error] 104-120: Promise executor functions should not be async.

(lint/suspicious/noAsyncPromiseExecutor)

🔇 Additional comments (2)
popup/welcome.html (2)

60-62: Info block addition looks good
The new informational panel clearly explains the need for the API key. Nice enhancement for user onboarding.


73-73: Verify primary-button styling
You added class="primary-button"—ensure this selector exists in popup.css (or inline styles) so the button renders as intended.

Comment on lines +4 to +11
static encryptApiKey(apiKey) {
try {
return btoa(apiKey);
} catch (error) {
console.error('Error encrypting API key:', error);
return null;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Base64 ≠ encryption – only obfuscation

Using btoa/atob provides zero cryptographic protection; anyone with DevTools can read the key.
If real secrecy is required, use the chrome.storage encrypted key store (on Firefox) or WebCrypto-based AES-GCM with a per-install random key.
At minimum, update comments and docs so users aren’t misled.

Also applies to: 14-21

🤖 Prompt for AI Agents
In utils/securityHelper.js around lines 4 to 11 and 14 to 21, the current
encryptApiKey method uses btoa which only obfuscates the API key rather than
encrypting it securely. Replace this with a proper encryption method such as
WebCrypto-based AES-GCM using a per-install random key or use the browser's
encrypted storage APIs like chrome.storage with encryption support.
Additionally, update comments and documentation to clarify that the current
method does not provide real encryption to avoid misleading users.

Comment on lines +31 to +38
if (sessionOnly) {
// Use sessionStorage for session-only storage
sessionStorage.setItem('groqApiKey', encryptedKey);
// Clear from local storage if it was stored there before
await chrome.storage.local.remove('groqApiKey');
// Set a flag to indicate we're using session storage
await chrome.storage.local.set({ 'apiKeyInSession': true });
} else {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

sessionStorage is not available in a background service-worker context

sessionStorage exists only in window-like contexts (popup, options, content scripts).
background.js is declared as a service_worker, which runs in a WorkerGlobalScope where sessionStorage is undefined.
Any attempt to call sessionStorage.setItem/getItem here will throw and break key persistence / retrieval.

Consider switching to chrome.storage.session (MV3-only) or keeping the “session-only” key in a module-level variable inside the service worker.

- sessionStorage.setItem('groqApiKey', encryptedKey);
+ await chrome.storage.session.set({ groqApiKey: encryptedKey });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (sessionOnly) {
// Use sessionStorage for session-only storage
sessionStorage.setItem('groqApiKey', encryptedKey);
// Clear from local storage if it was stored there before
await chrome.storage.local.remove('groqApiKey');
// Set a flag to indicate we're using session storage
await chrome.storage.local.set({ 'apiKeyInSession': true });
} else {
if (sessionOnly) {
// Use sessionStorage for session-only storage
await chrome.storage.session.set({ groqApiKey: encryptedKey });
// Clear from local storage if it was stored there before
await chrome.storage.local.remove('groqApiKey');
// Set a flag to indicate we're using session storage
await chrome.storage.local.set({ 'apiKeyInSession': true });
} else {
🤖 Prompt for AI Agents
In utils/securityHelper.js around lines 31 to 38, sessionStorage is used in a
background service-worker context where it is undefined, causing errors. Replace
sessionStorage usage with chrome.storage.session for session-only storage if
available, or alternatively store the session-only key in a module-level
variable within the service worker to maintain session scope without errors.

Comment on lines +17 to +19
// Load existing call count
this.loadCallCount();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

loadCallCount() is async but constructor doesn’t await it

apiCallCount may still be 0 when the first request finishes, causing an off-by-one write that overwrites the real persisted value.

Expose an explicit await manager.init() or make loadCallCount() synchronous by reading from storage only when first needed.

Also applies to: 91-99

🤖 Prompt for AI Agents
In utils/apiRequestManager.js around lines 17 to 19 and also 91 to 99, the
constructor calls the async loadCallCount() method without awaiting it, causing
apiCallCount to potentially be zero prematurely. To fix this, remove the async
call from the constructor and instead expose an explicit async init() method
that calls and awaits loadCallCount(). Ensure users of the class call await
manager.init() before making requests. Alternatively, refactor loadCallCount()
to be synchronous by deferring reading from storage until the value is first
needed.

Comment on lines +60 to +75
if (error.status === 429 && retryCount < this.options.maxRetries) {
const nextRetry = this.requestQueue.length;
const backoffDelay = this.options.initialBackoffDelay * Math.pow(2, retryCount);

console.log(`Rate limited. Retrying after ${backoffDelay}ms (Attempt ${retryCount + 1}/${this.options.maxRetries})`);

setTimeout(() => {
// Re-add this request to the queue with incremented retry count
this.requestQueue.splice(nextRetry, 0, {
fn,
resolve,
reject,
retryCount: retryCount + 1
});
}, backoffDelay);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Retry re-queued but never re-started

Inside the 429 handler you splice the request back into requestQueue, but you DON’T restart the processor if it has already exited (isProcessingQueue will soon be false).
Result: the retried request (and any queued after it) may never run.

        setTimeout(() => {
          this.requestQueue.splice(nextRetry, 0, {
            fn,
            resolve,
            reject,
            retryCount: retryCount + 1
          });
+         // Ensure the queue resumes
+         if (!this.isProcessingQueue) {
+           this.processQueue();
+         }
        }, backoffDelay);
🤖 Prompt for AI Agents
In utils/apiRequestManager.js around lines 60 to 75, the retry logic for
handling 429 errors re-queues the request using splice but does not restart the
queue processor if it has stopped, causing retries to never execute. To fix
this, after splicing the request back into the queue, check if the queue
processor is inactive (e.g., isProcessingQueue is false) and if so, call the
method that starts processing the queue to ensure retries are executed.

Comment on lines +85 to +96
// Listen for progress updates
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'updateProgress' && request.progress >= 0) {
progressText.textContent = `Processing: ${request.progress}%`;
} else if (request.action === 'updateProgress' && request.progress === -1) {
// Hide progress indicator
progressIndicator.style.opacity = '0';
setTimeout(() => progressIndicator.remove(), 300);
}
});

try {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid stacking message listeners – move this outside the function

chrome.runtime.onMessage.addListener is created every time translateParagraphs() runs. After a few page-level translations you will have N identical listeners, all firing for each progress message.

Extract the listener to module scope or register it once and remove it when progress === -1.

🤖 Prompt for AI Agents
In content.js around lines 85 to 96, the chrome.runtime.onMessage.addListener is
being added inside a function, causing multiple identical listeners to stack up
and fire repeatedly. To fix this, move the entire onMessage.addListener callback
outside of the translateParagraphs() function so it is registered only once at
the module level. Alternatively, register the listener once and remove it when
progress equals -1 to prevent multiple listeners from accumulating.

Comment on lines +156 to +185
// Apply exponential backoff on failure
if (retryCount < this.options.maxRetries) {
const backoffDelay = this.options.initialBackoffDelay * Math.pow(2, retryCount);
console.log(`API request failed, retrying in ${backoffDelay}ms...`, error);

// Push back into queue with increased retry count
this.requestQueue.unshift({
fn,
resolve,
reject,
retryCount: retryCount + 1
});

this.lastApiStatus = 'retrying';

setTimeout(() => {
this.processNextRequest();
}, backoffDelay);
} else {
console.error('API request failed after max retries:', error);
this.lastApiStatus = 'failed';
reject(error);
}
}

// Apply throttling delay before processing the next request
setTimeout(() => {
this.processNextRequest();
}, this.options.throttleDelay);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Retry branch schedules the queue twice

Inside the catch, when a retry is queued you call setTimeout(this.processNextRequest, backoffDelay), then execution continues to the common throttle setTimeout at 181-185. Two timers fire for the same queue item, potentially breaking ordering and inflating call counts.

Add return; after the back-off setTimeout to avoid double scheduling.

🤖 Prompt for AI Agents
In background.js between lines 156 and 185, the retry logic schedules
processNextRequest twice: once after the exponential backoff delay and again
immediately after with the throttle delay. To fix this, add a return statement
right after the backoff setTimeout call inside the retry branch to prevent the
function from continuing and scheduling the second timer, ensuring only one
retry timer is active per request.

Comment on lines +32 to +45
if (sessionOnly) {
// Use sessionStorage for session-only storage (not available in background script)
// Mark it for session-only in storage
await chrome.storage.local.set({
'groqApiKey': encryptedKey,
'apiKeyInSession': true
});
} else {
// Store in Chrome's local storage for persistence
await chrome.storage.local.set({
'groqApiKey': encryptedKey,
'apiKeyInSession': false
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

“Session only” key still lands in persistent storage

When sessionOnly === true the key is written to chrome.storage.local with a flag.
That storage survives browser restarts, so the key is in fact persistent.

Options:

  1. Use chrome.storage.session (MV3) for truly in-memory storage.
  2. Keep it only in a module-level variable inside the background page and drop it on unload.

Leaving it as-is defeats the whole privacy promise.

🤖 Prompt for AI Agents
In background.js around lines 32 to 45, the code stores session-only keys in
chrome.storage.local, which persists across browser restarts, violating the
session-only intent. To fix this, replace chrome.storage.local with
chrome.storage.session when sessionOnly is true to ensure in-memory,
non-persistent storage, or alternatively, store the key in a module-level
variable that clears on background page unload. Update the storage calls
accordingly to use chrome.storage.session for session-only keys.

Comment on lines +360 to +370
resetApiCountBtn.addEventListener('click', () => {
if (confirm("Reset API call counter to zero?")) {
setButtonLoading(resetApiCountBtn, true, "Resetting...");
chrome.runtime.sendMessage({action: "resetApiCount"}, (response) => {
setButtonLoading(resetApiCountBtn, false, "Reset Counter");
if (response && response.success) {
showMessage("API call counter reset successfully");
} else {
showMessage("Failed to reset API call counter", true);
}
updateApiUsageStats();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Counter reset message uses wrong action string

chrome.runtime.sendMessage({ action: "resetApiCount" }, …)
Background expects "resetApiCallCount". Update the string or the background listener so the feature works.

🧰 Tools
🪛 Biome (1.9.4)

[error] 365-365: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🤖 Prompt for AI Agents
In popup/popup.js around lines 360 to 370, the action string sent in
chrome.runtime.sendMessage is "resetApiCount" but the background script expects
"resetApiCallCount". Update the action string in the sendMessage call to
"resetApiCallCount" to match the background listener and ensure the reset
feature works correctly.

Comment on lines +74 to +75
<p class="note">Your key will be stored securely and only used for translation requests.</p>
<p class="note">Don't have a key? <a href="https://console.groq.com/keys" target="_blank">Click here</a></p>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add rel="noopener noreferrer" to external link
For security and to prevent reverse tabnabbing when using target="_blank", include rel="noopener noreferrer" on the anchor.

-<a href="https://console.groq.com/keys" target="_blank">Click here</a>
+<a href="https://console.groq.com/keys" target="_blank" rel="noopener noreferrer">Click here</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p class="note">Your key will be stored securely and only used for translation requests.</p>
<p class="note">Don't have a key? <a href="https://console.groq.com/keys" target="_blank">Click here</a></p>
<p class="note">Your key will be stored securely and only used for translation requests.</p>
<p class="note">Don't have a key? <a href="https://console.groq.com/keys" target="_blank" rel="noopener noreferrer">Click here</a></p>
🤖 Prompt for AI Agents
In popup/welcome.html at lines 74 to 75, the anchor tag with target="_blank"
lacks the rel="noopener noreferrer" attribute, which is necessary for security
to prevent reverse tabnabbing. Add rel="noopener noreferrer" to the anchor tag
linking to https://console.groq.com/keys to fix this issue.

Comment on lines +21 to +33
.note {
font-size: 0.85rem;
color: #666;
margin: 10px 0;
} input[type="password"], input[type="text"] {
width: 100%;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 13px;
margin-bottom: 12px;
height: 20px;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix CSS rule formatting to avoid parsing issues
On line 25 the closing brace and the next selector run together (} input[...]), which may break the stylesheet. Split them onto separate lines.

Apply this diff for clarity:

-    .note {
-      font-size: 0.85rem;
-      color: #666;
-      margin: 10px 0;
-    }    input[type="password"], input[type="text"] {
+    .note {
+      font-size: 0.85rem;
+      color: #666;
+      margin: 10px 0;
+    }
+
+    input[type="password"],
+    input[type="text"] {
      width: 100%;
      padding: 8px 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
      font-size: 13px;
      margin-bottom: 12px;
      height: 20px;
    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.note {
font-size: 0.85rem;
color: #666;
margin: 10px 0;
} input[type="password"], input[type="text"] {
width: 100%;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 13px;
margin-bottom: 12px;
height: 20px;
}
.note {
font-size: 0.85rem;
color: #666;
margin: 10px 0;
}
input[type="password"],
input[type="text"] {
width: 100%;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 13px;
margin-bottom: 12px;
height: 20px;
}
🤖 Prompt for AI Agents
In popup/welcome.html around lines 21 to 33, the closing brace of the .note CSS
rule and the next input selector are on the same line, which can cause parsing
issues. Fix this by placing the closing brace on its own line and starting the
input[type="password"], input[type="text"] selector on the next line to ensure
proper CSS formatting.

@saicharan-balina
Copy link
Author

I followed standard Chrome Extension practices, and I'm aware that btoa is not secure encryption. For full security, we can replace it with WebCrypto or chrome.storage.session. I prioritized getting a functional version ready first but can improve this further if needed.

@byteom
Copy link
Owner

byteom commented Jun 17, 2025

Resolve the conflicts

@saicharan-balina
Copy link
Author

✅ I’ve merged the latest changes from main into this branch (secure-api-key-and-throttle) locally. There were no conflicts, and everything is working as expected.

The working tree is clean (git status shows nothing to commit), and all features are ready for review.

Please take a look and let me know if anything else is needed. Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants