Skip to content

Open JabRef with a url handler#18

Open
LyzardKing wants to merge 1 commit into
mainfrom
url_handler
Open

Open JabRef with a url handler#18
LyzardKing wants to merge 1 commit into
mainfrom
url_handler

Conversation

@LyzardKing

@LyzardKing LyzardKing commented Jan 23, 2026

Copy link
Copy Markdown
Collaborator

User description

Proof of concept.

This needs a slight change in the jabref packaging, then we can use it to call jabref as a url handler.
Currently it could be used to start the program if not already active.

In theory this might make it so we can pass any cli arguments using the url handler. Asks for a permission on first run but can then be registered.

At the moment it's a bit flaky on whether it imports the bib item after starting, but should be fixable by just moving some code around.


PR Type

Enhancement


Description

  • Add protocol handler support to launch JabRef via jabref:// URL

  • Implement automatic retry mechanism with polling to detect JabRef startup

  • Fallback to protocol handler when HTTP connection fails initially

  • Configurable timeout and polling interval for server availability checks


Diagram Walkthrough

flowchart LR
  A["Connection attempt"] --> B{"HTTP connection<br/>successful?"}
  B -->|Yes| C["Connected to JabRef"]
  B -->|No| D["Trigger jabref://<br/>protocol handler"]
  D --> E["Poll HTTP server<br/>with timeout"]
  E --> F{"Server<br/>reachable?"}
  F -->|Yes| C
  F -->|No| G["Connection timeout"]
Loading

File Walkthrough

Relevant files
Enhancement
popup.js
Add protocol handler launch with server polling                   

popup.js

  • Added openJabRefAndWait() function to trigger JabRef via protocol
    handler and poll for server availability
  • Implements configurable timeout (default 10s) and polling interval
    (default 1s) for HTTP server detection
  • Modified connectToJabRef() to attempt protocol handler launch when
    initial HTTP connection fails
  • Provides user feedback via logging when attempting protocol handler
    and connection outcomes
+35/-0   

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unvalidated protocol handler invocation

Description: The code attempts to navigate to jabref:// protocol handler without validating the URL
scheme or implementing proper error handling, which could potentially be exploited to
trigger unintended protocol handlers if the URL is manipulated.
popup.js [145-149]

Referred Code
if (window.chrome && chrome.tabs && chrome.tabs.update) {
  try {
    await new Promise((resolve) =>
      chrome.tabs.update({ url: "jabref://", active: false }, () => resolve()),
    );
Localhost port scanning risk

Description: The polling loop makes repeated HTTP requests to a user-controlled port (from jabrefPort
storage) without rate limiting or request validation, potentially enabling SSRF attacks or
port scanning of localhost services.
popup.js [156-164]

Referred Code
while (Date.now() - start < timeout) {
  try {
    const resp = await fetch(base, { method: "GET", cache: "no-store" });
    if (resp && (resp.ok || resp.status === 404)) return true;
  } catch (e) {
    // still waiting
  }
  await new Promise((r) => setTimeout(r, interval));
}
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Silent fetch failures: The polling loop catches and silently ignores all fetch errors without logging, making
debugging connection issues difficult.

Referred Code
  const resp = await fetch(base, { method: "GET", cache: "no-store" });
  if (resp && (resp.ok || resp.status === 404)) return true;
} catch (e) {
  // still waiting
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated URL construction: The base URL parameter in openJabRefAndWait() is used directly in fetch without
validation, potentially allowing malicious URLs if the stored port value is compromised.

Referred Code
const resp = await fetch(base, { method: "GET", cache: "no-store" });
if (resp && (resp.ok || resp.status === 404)) return true;

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Avoid altering user's tab and abort fetch

Replace chrome.tabs.update with chrome.tabs.create to avoid navigating the
user's active tab away when triggering the protocol handler. Additionally, add
an AbortController to the polling fetch request to handle cancellation
gracefully if the popup closes.

popup.js [143-166]

 async function openJabRefAndWait(base, { timeout = 10000, interval = 1000 } = {}) {
   // Try to navigate to the protocol URL to trigger JabRef
-  if (window.chrome && chrome.tabs && chrome.tabs.update) {
+  if (window.chrome && chrome.tabs && chrome.tabs.create) {
     try {
       await new Promise((resolve) =>
-        chrome.tabs.update({ url: "jabref://", active: false }, () => resolve()),
+        chrome.tabs.create({ url: "jabref://", active: false }, () => resolve()),
       );
     } catch (e) {
-      console.warn("chrome.tabs.update failed", e);
+      console.warn("chrome.tabs.create failed", e);
     }
   }
 
   const start = Date.now();
+  const controller = new AbortController();
+  const signal = controller.signal;
+
+  // Clean up the AbortController when the popup closes.
+  window.addEventListener("unload", () => controller.abort(), { once: true });
+
   while (Date.now() - start < timeout) {
     try {
-      const resp = await fetch(base, { method: "GET", cache: "no-store" });
+      const resp = await fetch(base, { method: "GET", cache: "no-store", signal });
       if (resp && (resp.ok || resp.status === 404)) return true;
     } catch (e) {
+      if (e.name === 'AbortError') {
+        console.log('Fetch aborted in openJabRefAndWait.');
+        return false;
+      }
       // still waiting
     }
     await new Promise((r) => setTimeout(r, interval));
   }
+  controller.abort(); // Abort any ongoing fetch if timeout is reached
   return false;
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a major usability issue where chrome.tabs.update would disrupt the user's active tab and proposes using chrome.tabs.create as a less intrusive alternative. It also enhances robustness by adding an AbortController to manage the fetch request lifecycle, preventing potential errors if the popup is closed.

High
  • More

@koppor

koppor commented Jan 23, 2026

Copy link
Copy Markdown
Member

This needs a slight change in the jabref packaging, then we can use it to call jabref as a url handler.

Nice! This is I also thought for the VS Code extension - cc @palukku

Comment thread popup.js
if (window.chrome && chrome.tabs && chrome.tabs.update) {
try {
await new Promise((resolve) =>
chrome.tabs.update({ url: "jabref://", active: false }, () => resolve()),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thinking aloud:

Wouldn't it even possible to pass the import here? - OK, this would mean: back to native messaging "kind of"? -- Meaning: the URL handler is surely able to pass a command line argument to JabRef - and JabGui could be able to handle some arguments passing from the browser extension - no sophisticated "nice" CLI, maybe just process-browser-extension-url and then as additional parameter some URL, which is then internally parsed by JabRef. -- OK, then we have the issue with "command line too long". Then we are back on writing temporary files :)

Maybe, we need "web sockets" again -- if JabRef is started via the browser extension, JabRef could send a web socket call to the browser extension to say: Hey, I am ready to process. OK, but this needs a connection started from the browser extension - and we are back to 0.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I was thinking the same thing.
It would mean managing the message in jabref, as by default it doesn't read the part after the // as the cli arguments (not sure why).

If so we could use the --importBibtex using this method, not needing the server to be active.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alternative:

UrlHandler as seperate program.

  • Running JabRef - just continuing
  • Non-Running JabRef: Start http server without UI.

But maybe not good if user then starts UI manually, this could be confusing - and we would need to update the http handling to work non-gui.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was thinking the same thing. It would mean managing the message in jabref, as by default it doesn't read the part after the // as the cli arguments (not sure why).

If so we could use the --importBibtex using this method, not needing the server to be active.

TBH, I like it better if the URL handler would just start JabRef and we would use HTTP for the "real" communication... Don't want to fiddle around with URL Handler issues (maybe even different browsers doing differently) - and maintaining an additional CLI interface.

Idea: "PING" JabRef on start of Browser (if enabled by the user)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's the problem with native messaging? It's the "official" method for browser-extension-to-app communication, and the current solution works relatively reliable. Since it would be only a fall-back in case of deactivated http server, this seems to me a fine path. And we already have the code for it, and it would be backwards compatible with a bit older JabRef versions...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This might be more of a problem on linux, but with different systems/install methods things get complicated.
Url handlers could be the next best thing, althought they are limited in length (especially on windows).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I feel like on Windows it's the opposite.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also mentioned at JabRef/JabRef-Browser-Extension#624 (comment).


Here on Windows, the new extension works very well.

My current reading is that it "only" causes trouble if the user did not start JabRef GUI when clicking on the import symbol. The decision to take is know to a) ensure that a newly started JabRef GUI accepts the http call properly or b) re-introduce native messaging for the newly started case (and maintain that interface including installation challenges.

To get an impression of the effort on JabRef's side regarding native messaging see the PR where we removed that: JabRef/jabref#14884

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants