-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
.pr_agent_auto_best_practices
Pattern 1: In unit tests, assert exact expected values (or the full structured output) instead of weak predicate checks like contains(...), substring matches, or isPresent() followed by get(); prefer direct equality/Optional assertions that would fail on near-miss regressions.
Example code before:
Optional<String> url = service.findPdfUrl();
assertTrue(url.isPresent());
assertTrue(url.get().contains("pdf"));
Example code after:
Optional<String> url = service.findPdfUrl();
assertEquals(Optional.of("https://example.org/scidir-pdf/123"), url);
Relevant past accepted suggestions:
Suggestion 1:
[correctness] Weak assertion on PDF URL
Weak assertion on PDF URL
The new unit test asserts only that the result URL `contains("pdfft")`, which can pass for incorrect URLs and weakens regression protection. JabRef JUnit conventions prefer direct content assertions over indirect `assertTrue` predicate checks.The test findsPdfDirectlyWhenSciDirPdfPresent uses weak predicate assertions (isPresent() and contains(&amp;quot;pdfft&amp;quot;)) instead of verifying the exact URL returned.
This behavior change is intended to return the scidir-pdf link directly. The test should assert the full expected URL (or at minimum equality to the mocked @href value) to reliably prevent regressions.
- jablib/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java[91-115]
Suggestion 2:
[correctness] Optional asserted via get()
Optional asserted via get()
The new test asserts `Optional` presence via `isPresent()` and then calls `get()`, which is a discouraged Optional usage and makes assertions less direct. Use an equality assertion against the expected `Optional` value instead.The test uses assertTrue(result.isPresent()) followed by result.get(), which is less direct and relies on unsafe access patterns.
JUnit assertions should prefer direct value/content comparisons, and Optional usage should avoid isPresent()/get() when an equality assertion is clearer.
- jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/AllCitationFetcherTest.java[61-71]
Suggestion 3:
[correctness] Test uses `orElse("")`
Test uses `orElse("")`
The new tests assert citation keys via `Optional.orElse("")`, which converts absence into a sentinel value and is discouraged by the Optional usage guideline. This can hide intent and makes assertions less precise than asserting on the Optional directly.The new tests use Optional.orElse(&quot;&quot;) when asserting citation keys. This introduces a sentinel default and is discouraged by the project Optional guideline.
BibEntry.getCitationKey() returns an Optional, so the tests can assert Optional.of(&quot;...&quot;) for present values and Optional.empty() for absent values.
- jablib/src/test/java/org/jabref/logic/importer/fileformat/CitaviXmlImporterTest.java[59-61]
- jablib/src/test/java/org/jabref/logic/importer/fileformat/CitaviXmlImporterTest.java[101-103]
Suggestion 4:
[reliability] Weak month assertion
Weak month assertion
The added test checks only for substring "jan", which is also present in the original value "January". This can let failures in `normalize_month` slip through without being detected.The month normalization test can pass even if the month was not normalized, because it only checks for the substring jan.
normalize_month produces JabRef month format like #jan#, while the original input January also contains jan.
- jabkit/src/test/java/org/jabref/toolkit/commands/ConvertTest.java[144-147]
Replace the substring assertion with something that distinguishes normalized vs unnormalized, e.g.:
-
assertTrue(outputContent.contains(&quot;#jan#&quot;));(or assert on the exactmonthfield line if stable).
Suggestion 5:
[correctness] Missing unicode dash testcases
Missing unicode dash testcases
Behavior in `FirstPage`/`LastPage` was changed to handle en-dash/em-dash page ranges, but the existing unit tests were not updated to cover these new inputs. This risks regressions and leaves the reported bug scenario unverified by automated tests.FirstPage and LastPage were updated to split page ranges on Unicode en-dash/em-dash, but the existing unit tests only cover ASCII hyphen variants. Add/extend tests so the new behavior is verified and regressions are prevented.
The production code now uses a split regex including \u2013 and \u2014, which changes parsing behavior in org.jabref.logic.
- jablib/src/test/java/org/jabref/logic/layout/format/FirstPageTest.java[23-30]
- jablib/src/test/java/org/jabref/logic/layout/format/LastPageTest.java[23-31]
Pattern 2: Avoid hard-coded upstream identifiers and magic strings in workflows/tests; derive repository owner/name/URLs from runtime context (e.g., GitHub Actions variables) and reuse shared constants to prevent drift and fork-incompatibilities.
Example code before:
String image = "ghcr.io/UpstreamOrg/" + component;
when(prefs.getApiKey("Scopus")).thenReturn("key");
Example code after:
String image = "ghcr.io/" + repoOwner + "/" + component;
when(prefs.getApiKey(Scopus.FETCHER_NAME)).thenReturn("key");
Relevant past accepted suggestions:
Suggestion 1:
[correctness] Fork images use upstream names
Fork images use upstream names
After removing the job-level repository guard, the workflow runs in forks but still generates tags for `ghcr.io/JabRef/...` and labels pointing to `https://github.com/JabRef/jabref`, so fork builds produce incorrect image metadata and cannot publish correctly-namespaced images on fork `push`/`tag` events.The Docker Images workflow now runs in forks (job-level guard removed), but still hard-codes the upstream GHCR namespace (ghcr.io/JabRef/...) and upstream org.opencontainers.image.source label. This causes fork builds to generate incorrect tags/labels and prevents forks from producing correctly-namespaced images on fork push/tag events.
The workflow already gates login/push to upstream-only, but the metadata generation is still upstream-specific.
- .github/workflows/dockerimages.yml[31-45]
- Change
images:to use the current repo owner dynamically (e.g.,ghcr.io/${{ github.repository_owner }}/${{ matrix.component }}). - Change
org.opencontainers.image.sourceto use the current repo (e.g.,https://github.com/${{ github.repository }}). - .github/workflows/dockerimages.yml[46-88]
- Revisit login/push gating if the intent is to allow forks to publish on their own
push/tagevents (otherwise keep upstream-only, but ensure tags/labels are still fork-correct for non-push builds).
Suggestion 2:
[maintainability] Magic string `"Scopus"` in test
Magic string `"Scopus"` in test
The modified test stubs `getApiKey` using the literal `"Scopus"` instead of reusing the existing fetcher constant, increasing risk of typos and drift if the name changes. This reduces maintainability and consistency across the codebase.ScienceDirectTest hardcodes the fetcher key name as &amp;quot;Scopus&amp;quot;, duplicating the value instead of using the existing shared constant.
A constant already exists (Scopus.FETCHER_NAME). Using it reduces the risk of typos and keeps tests aligned if the fetcher name ever changes.
- jablib/src/test/java/org/jabref/logic/importer/fetcher/ScienceDirectTest.java[42-46]
- jablib/src/main/java/org/jabref/logic/importer/fetcher/Scopus.java[32-33]
Suggestion 3:
[correctness] Wrong default database mode
Wrong default database mode
In JabRefFrameViewModel.addParserResult, when no library is open it creates a new BibDatabaseContext without setting the mode from preferences, so it defaults to BibLaTeX even if the user’s default is BibTeX. This causes ImportCleanup (and the subsequent import dialog flow) to operate under an unintended mode, impacting imported field/entry handling.When importing into the “current library” with no library tab open, addParserResult creates a new BibDatabaseContext() but does not set its mode from user preferences. BibDatabaseContext then defaults to BIBLATEX, which can make ImportCleanup and the import flow behave incorrectly for users whose default mode is BibTeX.
Other new-library creation paths (e.g., NewDatabaseAction) explicitly set the mode via preferences.getLibraryPreferences().getDefaultBibDatabaseMode(). The import code immediately uses libraryTab.getBibDatabaseContext().getMode() to drive cleanup.
- jabgui/src/main/java/org/jabref/gui/frame/JabRefFrameViewModel.java[379-383]
Set the mode right after creating the context:
Pattern 3: Never mutate lists/collections that may be immutable (e.g., List.of(), preference getters, or observable wrappers); defensively copy into a mutable collection before calling add, addAll, or wrapping as an observable list.
Example code before:
List<String> cycle = prefs.getStringList("previewCycle"); // may be List.of()
if (cycle.isEmpty()) {
cycle.addAll(List.of("Preview", "BibTeX"));
}
Example code after:
List<String> cycle = new ArrayList<>(prefs.getStringList("previewCycle"));
if (cycle.isEmpty()) {
cycle.addAll(List.of("Preview", "BibTeX"));
}
Relevant past accepted suggestions:
Suggestion 1:
[reliability] Preview cycle addAll crash
Preview cycle addAll crash
JabRefGuiPreferences#getPreviewLayouts mutates the List returned by getStringList(PREVIEW_CYCLE); when the stored value is blank, getStringList returns an immutable List.of(), so cycle.addAll(...) throws UnsupportedOperationException and preview preferences fail to load.JabRefGuiPreferences#getPreviewLayouts calls cycle.addAll(...) when the cycle is empty. When the stored preference is blank, getStringList(...) returns an immutable List.of(); mutating it throws UnsupportedOperationException and prevents preview preferences from loading.
Reset currently writes an empty cycle to the backing store, making it likely that PREVIEW_CYCLE becomes blank and triggers this path.
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[822-869]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[856-862]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[284-306]
Suggestion 2:
[reliability] Immutable BST paths list
Immutable BST paths list
PreviewPreferences wraps bstPreviewLayoutPaths with FXCollections.observableList; when constructed from PreviewPreferences.getDefault() (List.of()), later mutations like setBstPreviewLayoutPaths can throw UnsupportedOperationException on fresh profiles where PREVIEW_BST_LAYOUT_PATHS is absent.bstPreviewLayoutPaths is currently an observable view over the passed-in list. When that passed-in list is immutable (e.g., List.of() from defaults), later mutations (setAll, setBstPreviewLayoutPaths) can throw UnsupportedOperationException.
Fresh profiles without PREVIEW_BST_LAYOUT_PATHS use defaults.getBstPreviewLayoutPaths(), which originates from PreviewPreferences.getDefault() and currently uses List.of().
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[30-56]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[829-836]
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[146-152]
Suggestion 3:
[reliability] Preview cycle addAll crash
Preview cycle addAll crash
JabRefGuiPreferences#getPreviewLayouts mutates the List returned by getStringList(PREVIEW_CYCLE); when the stored value is blank, getStringList returns an immutable List.of(), so cycle.addAll(...) throws UnsupportedOperationException and preview preferences fail to load.JabRefGuiPreferences#getPreviewLayouts calls cycle.addAll(...) when the cycle is empty. When the stored preference is blank, getStringList(...) returns an immutable List.of(); mutating it throws UnsupportedOperationException and prevents preview preferences from loading.
Reset currently writes an empty cycle to the backing store, making it likely that PREVIEW_CYCLE becomes blank and triggers this path.
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[822-869]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[856-862]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[284-306]
Suggestion 4:
[reliability] Immutable BST paths list
Immutable BST paths list
PreviewPreferences wraps bstPreviewLayoutPaths with FXCollections.observableList; when constructed from PreviewPreferences.getDefault() (List.of()), later mutations like setBstPreviewLayoutPaths can throw UnsupportedOperationException on fresh profiles where PREVIEW_BST_LAYOUT_PATHS is absent.bstPreviewLayoutPaths is currently an observable view over the passed-in list. When that passed-in list is immutable (e.g., List.of() from defaults), later mutations (setAll, setBstPreviewLayoutPaths) can throw UnsupportedOperationException.
Fresh profiles without PREVIEW_BST_LAYOUT_PATHS use defaults.getBstPreviewLayoutPaths(), which originates from PreviewPreferences.getDefault() and currently uses List.of().
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[30-56]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[829-836]
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[146-152]
Suggestion 5:
[reliability] Preview cycle addAll crash
Preview cycle addAll crash
JabRefGuiPreferences#getPreviewLayouts mutates the List returned by getStringList(PREVIEW_CYCLE); when the stored value is blank, getStringList returns an immutable List.of(), so cycle.addAll(...) throws UnsupportedOperationException and preview preferences fail to load.JabRefGuiPreferences#getPreviewLayouts calls cycle.addAll(...) when the cycle is empty. When the stored preference is blank, getStringList(...) returns an immutable List.of(); mutating it throws UnsupportedOperationException and prevents preview preferences from loading.
Reset currently writes an empty cycle to the backing store, making it likely that PREVIEW_CYCLE becomes blank and triggers this path.
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[822-869]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[856-862]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[284-306]
Suggestion 6:
[reliability] Immutable BST paths list
Immutable BST paths list
PreviewPreferences wraps bstPreviewLayoutPaths with FXCollections.observableList; when constructed from PreviewPreferences.getDefault() (List.of()), later mutations like setBstPreviewLayoutPaths can throw UnsupportedOperationException on fresh profiles where PREVIEW_BST_LAYOUT_PATHS is absent.bstPreviewLayoutPaths is currently an observable view over the passed-in list. When that passed-in list is immutable (e.g., List.of() from defaults), later mutations (setAll, setBstPreviewLayoutPaths) can throw UnsupportedOperationException.
Fresh profiles without PREVIEW_BST_LAYOUT_PATHS use defaults.getBstPreviewLayoutPaths(), which originates from PreviewPreferences.getDefault() and currently uses List.of().
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[30-56]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[829-836]
- jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java[146-152]
Suggestion 7:
[reliability] Preview cycle addAll crash
Preview cycle addAll crash
JabRefGuiPreferences#getPreviewLayouts mutates the List returned by getStringList(PREVIEW_CYCLE); when the stored value is blank, getStringList returns an immutable List.of(), so cycle.addAll(...) throws UnsupportedOperationException and preview preferences fail to load.JabRefGuiPreferences#getPreviewLayouts calls cycle.addAll(...) when the cycle is empty. When the stored preference is blank, getStringList(...) returns an immutable List.of(); mutating it throws UnsupportedOperationException and prevents preview preferences from loading.
Reset currently writes an empty cycle to the backing store, making it likely that PREVIEW_CYCLE becomes blank and triggers this path.
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[822-869]
- jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java[856-862]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[284-306]
Pattern 4: Keep user-facing documentation and release notes professional, precise, and consistently formatted: fix grammar/typos, avoid overly technical wording, ensure headings/sections are correct, and keep issue/PR references and Markdown links valid and consistent.
Example code before:
### Added
-Added fix for NPE warnings [#15127][https://github.com/org/repo/issues/15283]
Example code after:
### Fixed
- Fixed a crash when opening the notification dialog. [#15127](https://github.com/org/repo/issues/15127)
Relevant past accepted suggestions:
Suggestion 1:
[maintainability] Slang term `AI slop`
Slang term `AI slop`
The Code of Conduct includes the slang term `AI slop`, which is potentially unprofessional for user-facing documentation. This may reduce clarity and tone consistency in official community guidelines.The Code of Conduct uses the slang phrase AI slop, which can be perceived as unprofessional or unclear in official documentation.
User-facing documentation should use professional, precise wording.
- CODE_OF_CONDUCT.md[53-56]
Suggestion 2:
[maintainability] FAQ Gradle section grammar issues
FAQ Gradle section grammar issues
New documentation text contains grammar and capitalization issues (e.g., `Gradle builds files`, `gradle run`, and `development versions of gradle`). This violates the requirement that user-facing text be professional and free of typos/grammar errors.The newly added Gradle FAQ text has grammar/capitalization issues (e.g., Gradle builds files, gradle run, and development versions of gradle), which makes the documentation look unpolished.
This is user-facing documentation and must be professional and free of typos/grammar errors.
- docs/code-howtos/faq.md[20-35]
Suggestion 3:
[maintainability] Empty compilation section
Empty compilation section
In docs/code-howtos/faq.md, the new "Compilation" heading has no content because the next heading is also level-2, so the intended subsection is not nested and the section hierarchy/TOC becomes misleading. This appears to be an oversight introduced when adding the new parent "Compilation" section around the existing entry.## Compilation is immediately followed by another ## heading, which makes the Compilation section empty and breaks the document hierarchy.
This section appears to be a new parent grouping for an existing FAQ entry; the child heading should be nested.
- docs/code-howtos/faq.md[193-199]
Change ## \java: package org.jabref.logic.journals does not exist`to### ...(and optionally add a short intro line under## Compilation`).
Suggestion 4:
[correctness] Changelog misses DOI fix
Changelog misses DOI fix
The changelog addition documents an entry-editor closing fix, but it does not document the DOI case-insensitive duplicate-detection fix introduced in this PR. This can lead to incomplete or misleading release notes for users.The changelog update in this PR documents an entry-editor closing fix, but does not document the DOI case-insensitivity duplicate-detection fix that this PR introduces.
Release notes should reflect user-visible bug fixes; this PR changes duplicate detection behavior for DOI matching.
- CHANGELOG.md[32-37]
- jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java[75-79]
Suggestion 5:
[correctness] Changelog entry lacks issue link
Changelog entry lacks issue link
The new `CHANGELOG.md` entry omits the issue/PR reference and uses awkward phrasing (`quick setting`, `cover images download`) compared to surrounding entries. This reduces professionalism and traceability of user-facing release notes.The added CHANGELOG.md entry is missing an issue/PR reference and the wording is imprecise (quick setting, cover images download).
Other entries in the same section include issue references (e.g., [#15134], [#13821]). This entry should match that convention and be proofread.
- CHANGELOG.md[16-19]
Suggestion 6:
[correctness] Changelog too technical
Changelog too technical
The new `CHANGELOG.md` entry includes highly technical/internal terms (`index exceeds maxCellCount`, `StackOverflowError`) and may be imprecise about the log level/wording (“warnings”), reducing clarity for end users. This risks violating the requirement that release notes be end-user focused and precisely worded.The new CHANGELOG.md entry is likely too technical for end users (mentions internal JavaFX message text and StackOverflowError) and may be imprecise about what was suppressed.
Changelog entries should be understandable by average users and use precise wording.
- CHANGELOG.md[19-19]
Suggestion 7:
[correctness] Changelog entry lacks issue link
Changelog entry lacks issue link
The new CHANGELOG entry has no issue reference and contains slightly unpolished wording (missing article and inconsistent provider name). This reduces traceability and professionalism of user-facing release notes.The added CHANGELOG.md entry is user-facing text but is missing an issue reference and has minor grammar/wording issues.
Other entries in the same section include an issue link (e.g., [#15134](...)). The added entry should follow the same traceability and wording quality.
- CHANGELOG.md[16-16]
Suggestion 8:
[correctness] Changelog link/wording incorrect
Changelog link/wording incorrect
The new CHANGELOG entry is developer-focused/grammatically incorrect and references `#15127` but links to a different issue (`/issues/15283`), which can mislead users. This does not meet the project requirements for user-facing, professional CHANGELOG text with correct references.The new CHANGELOG.md entry is not user-focused (mentions NPE/TaskNotification), contains grammatical issues, and the displayed issue reference #15127 links to /issues/15283.
Compliance requires CHANGELOG entries to be understandable to average users and user-facing text to be precise and correctly referenced.
- CHANGELOG.md[31-31]
Suggestion 9:
[correctness] Changelog bullet missing space
Changelog bullet missing space
The new changelog bullet is formatted as `-Added ...` without a space after `-`, which breaks the project’s changelog formatting conventions. This is user-facing text and should be cleanly formatted.The added changelog entry is missing a space after the list marker (-).
The changelog is user-facing and should follow consistent Markdown list formatting.
- CHANGELOG.md[15-15]
Suggestion 10:
[correctness] Changelog misstates ESC behavior
Changelog misstates ESC behavior
The new CHANGELOG entry claims that pressing ESC closes the global search dialog, but the updated implementation consumes the CLEAR_SEARCH/ESC event when the field is non-empty (clearing it instead). This can mislead users about the actual two-step ESC behavior (first clears, then closes).The CHANGELOG entry says pressing ESC closes the global search dialog, but the implementation clears (and consumes) the event when the input is non-empty, so ESC does not close the dialog on the first press in that case.
This is user-facing release-note text and should precisely match actual behavior.
- CHANGELOG.md[14-14]
Suggestion 11:
[correctness] Changelog entry miscategorized
Changelog entry miscategorized
The changelog entry describes a bug fix but is placed under the "Added" section instead of "Fixed", reducing release note consistency.A bug-fix changelog entry is currently under the “### Added” section.
The line begins with “We fixed an issue…”, which belongs under “### Fixed” per Keep a Changelog conventions used in this file.
- CHANGELOG.md[10-35]
Suggestion 12:
[correctness] Awkward CHANGELOG wording
Awkward CHANGELOG wording
The new CHANGELOG entry contains grammatically incorrect/awkward phrasing (`will show up`) and imprecise wording (`side panels down/up`) in user-facing text. This reduces clarity and professionalism of release notes.The CHANGELOG entry added in this PR is user-facing but has awkward/incorrect grammar (e.g., will show up) and imprecise phrasing (down/up, side panels).
Compliance requires user-facing text (including CHANGELOG entries) to be typo-free, grammatically correct, and precise.
- CHANGELOG.md[29-29]
Suggestion 13:
[correctness] Broken `#15195` changelog link
Broken `#15195` changelog link
The new changelog entry uses invalid Markdown link syntax for the referenced issue. This makes the issue reference non-clickable and reduces the quality of user-facing release notes.The CHANGELOG entry uses incorrect Markdown link syntax: [#15195][https://...].
Changelog entries are user-facing text and should be correctly formatted and professional.
- CHANGELOG.md[16-16]
Suggestion 14:
[correctness] Changelog uses `Latex` spelling
Changelog uses `Latex` spelling
The new changelog entry uses `Latex` instead of the standard `LaTeX` spelling, which is a typographical/style mistake in user-facing documentation. This reduces professionalism and consistency of written content.The changelog entry contains a typographical/style issue: Latex should be LaTeX.
This is user-facing documentation and should match established terminology/capitalization used in the project.
- CHANGELOG.md[45-45]
Suggestion 15:
[correctness] Changelog link format
Changelog link format
The new CHANGELOG entry uses inconsistent issue link formatting and inconsistent wording/capitalization compared to adjacent entries and the UI tab name, reducing documentation consistency.The new CHANGELOG entry is inconsistent with surrounding entries: it uses [15117](...) instead of [#15117](...), and it refers to “External File Type” (singular, title case) while the UI uses “External file types”.
This is a documentation-only consistency fix; no runtime behavior changes required.
- CHANGELOG.md[49-49]
- jabgui/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java[35-38]
Pattern 5: Sanitize and correctly escape external/user-controlled text when generating filenames, paths, CLI parsing, or serialized output (YAML/Lucene/etc.); prevent path traversal/collisions and prefer robust escaping/serialization helpers over ad-hoc string concatenation.
Example code before:
Path md = outputDir.resolve(citeKey + ".md"); // citeKey may contain "../"
String yaml = "title: '" + title.replace("'", "\\'") + "'\n";
String query = term.replace("/", "/"); // incomplete escaping
Example code after:
String safeKey = FileNameCleaner.cleanFileName(citeKey).replace("..", "").replace("/", "_");
Path md = outputDir.resolve(safeKey + ".md").normalize();
if (!md.startsWith(outputDir)) {
throw new IllegalArgumentException("Invalid output path");
}
Map<String, Object> fm = Map.of("title", title, "permalink", "/" + safeKey + "/");
String yaml = new Yaml().dump(fm);
Relevant past accepted suggestions:
Suggestion 1:
[security] Path traversal via citekey
Path traversal via citekey
Raw citation keys are used to form .md/.bib/.pdf filenames and resolved paths. Since citation keys are not restricted from containing path separators, a crafted/dirty key can escape the export directory or create invalid paths, resulting in arbitrary file write outside the target folder or export failures.AcademicPagesExporter builds output file paths from citationKey without sanitization. Citation keys can contain path separators (e.g., /) or traversal segments (..), so outputDir.resolve(citationKey + &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pdf&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) can write outside files/.
JabRef’s citation key validation does not disallow / (see DISALLOWED_CHARACTERS), so keys are not guaranteed safe as filenames.
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[102-105]
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[206-224]
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[232-240]
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[176-191]
- Create a helper like
sanitizeForFileName(String)using existing utilities (e.g.,FileNameCleaner.cleanFileName/FileUtil.getValidFileName) and additionally strip path separators and... - When resolving, normalize and assert containment:
Path target = outputDir.resolve(safeName).normalize();if (!target.startsWith(outputDir)) { /* skip or fail */ }
- Use the sanitized key for
.md/.bib/.pdffilenames; consider a separate URL slug (or encode) forpermalink:andpaperurl/bibtexurl.
Suggestion 2:
[correctness] Invalid YAML quoting
Invalid YAML quoting
YAML front matter is hand-built and uses `\'` to escape apostrophes inside single-quoted scalars. This is not valid YAML single-quote escaping (should be doubled), and unquoted permalink values can also break YAML if the key contains special characters (e.g., ':' or '#').Front matter YAML is built via string concatenation and uses incorrect escaping for apostrophes in single-quoted scalars, and permalink is emitted unquoted while embedding the citation key.
The project already uses SnakeYAML in CffExporter, which avoids these escaping pitfalls.
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[171-198]
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[265-271]
- Preferred: build a
Map&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;String, Object&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;of front-matter fields and dump it via SnakeYAML (then wrap with---lines). - If staying with strings: change single-quote escaping to YAML-compliant
value.replace(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;, &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;)and quotepermalink:(and possiblycollection/category) as needed.
Suggestion 3:
[reliability] Output overwrites on collisions
Output overwrites on collisions
Entries missing citation keys (or with duplicate keys) will overwrite each other’s markdown/PDF/BibTeX outputs because filenames are derived solely from date + key (or "unknown") and PDFs/BIBs are just `key.pdf` / `key.bib`.Filename scheme can collide (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;unknown&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; fallback; duplicate keys), causing silent overwrites of .md/.bib/.pdf outputs.
JabRef supports duplicate detection for citation keys (see getNumberOfCitationKeyOccurrences), so collisions are possible.
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[102-105]
- jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesExporter.java[221-244]
- If citation key is missing, generate a stable unique suffix (e.g., entry ID) for filenames.
- If a target path already exists, append
-a,-b, ... (similar to citation key generator) instead of overwriting. - Consider keeping
REPLACE_EXISTINGonly if user explicitly opts in.
Suggestion 4:
[correctness] Incomplete key sanitization
Incomplete key sanitization
CitaviXmlImporter strips whitespace from but leaves other BibTeX-disallowed characters (e.g., comma, braces) intact. Since BibEntryWriter writes the citation key verbatim into the entry header, such keys can produce malformed .bib output (e.g., "@article{Smith,2020,"), and will also be flagged by JabRef’s citation-key validity checker.CitaviXmlImporter currently strips whitespace from &lt;CitationKey&gt; but does not remove BibTeX-disallowed characters (e.g., ,, {, }), which can lead to malformed BibTeX output because the key is written verbatim in @type{&lt;key&gt;,.
JabRef already centralizes citation key cleaning rules in CitationKeyGenerator and uses them elsewhere (e.g., EndnoteImporter). BibEntryWriter writes the citation key without escaping.
- jablib/src/main/java/org/jabref/logic/importer/fileformat/CitaviXmlImporter.java[522-528]
- jablib/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java[33-38]
- jablib/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java[94-114]
- jablib/src/main/java/org/jabref/logic/importer/fileformat/EndnoteImporter.java[238-240]
Replace the replaceAll(&quot;\\s+&quot;, &quot;&quot;) mapping with a call to CitationKeyGenerator.removeUnwantedCharactersWithKeepDiacritics(...) (and optionally DEFAULT_UNWANTED_CHARACTERS) so that whitespace and disallowed characters are removed. After cleaning, re-check blankness before setting the key.
Add a unit test covering e.g. &lt;CitationKey&gt;Smith, 2020&lt;/CitationKey&gt; to ensure the imported key becomes Smith2020 (or whatever policy you decide) and does not produce an invalid key per ValidCitationKeyChecker.
Suggestion 5:
[correctness] Incomplete Lucene escaping
Incomplete Lucene escaping
The new escapeForLucene() does not escape some characters that are valid in Search.g4 TERM (e.g., '"' and '/'), so valid user input can be converted into invalid/altered Lucene syntax. This can cause ParseException and return empty results, or accidentally form a regex-delimited query because this code itself uses /.../ to represent regex queries.escapeForLucene is intended to replace QueryParser.escape while preserving * and ? wildcards, but it currently escapes an incomplete set of Lucene-special characters. Because the search grammar allows these characters inside TERM, some valid user searches can be converted to invalid/altered Lucene syntax, causing ParseException and silently returning empty results.
-
TERMallows many characters (only excludes whitespace and=!~()), so users can enter quotes or slashes without quoting. -
SearchToLuceneVisitoritself uses/.../as a regex delimiter, so leaving/unescaped in non-regex terms can create ambiguous output. -
LinkedFilesSearcherswallows parse errors and returns empty results.
- jablib/src/main/java/org/jabref/logic/search/query/SearchToLuceneVisitor.java[61-120]
- jablib/src/main/antlr/org/jabref/search/Search.g4[42-45]
- jablib/src/test/java/org/jabref/logic/search/query/SearchQueryLuceneConversionTest.java[15-60]
- jablib/src/main/java/org/jabref/logic/search/retrieval/LinkedFilesSearcher.java[84-91]
- Update
escapeForLuceneto escape the full Lucene reserved set (including at least&amp;amp;quot;and/), while deliberately NOT escaping*and?. - Consider implementing as:
QueryParser.escape(term)then selectively unescape\*-&amp;gt;*and\?-&amp;gt;?. - Add unit tests for unquoted terms containing
&amp;amp;quot;and/(and ensure they don’t cause parse failures).
Suggestion 6:
[correctness] `convert` formatter parsing broken
`convert` formatter parsing broken
The new `--field-formatters` parsing replaces all commas, which breaks specifying multiple formatters within a single field (commas inside `[...]`). This can silently apply the wrong formatter(s) or none at all, producing incorrect export output.--field-formatters currently does fieldFormatters.replace(&quot;,&quot;, &quot;\n&quot;), which corrupts valid formatter specs that contain comma-separated formatter lists inside brackets (e.g., title[escapeAmpersands,latex_cleanup]). This can lead to silently wrong/no cleanup actions.
This CLI argument is external input and should be parsed robustly. The current approach cannot distinguish commas separating field blocks from commas separating formatters within a field.
- jabkit/src/main/java/org/jabref/toolkit/commands/Convert.java[72-80]
- jabkit/src/main/java/org/jabref/toolkit/commands/GenerateBibFromAux.java[86-94]
[Auto-generated best practices - 2026-03-25]
- Home
- General Information
- Development
- Please go to our devdocs at https://devdocs.jabref.org
- Completed "Google Summer of Code" (GSoC) projects
- GSoC 2025 ‐ Git Support for JabRef
- GSoC 2025 - LSP
- GSoC 2025 - Walkthrough and Welcome Tab
- GSoC 2024 ‐ Improved CSL Support (and more LibreOffice‐JabRef integration enhancements)
- GSoC 2024 - Lucene Search Backend Integration
- GSoC 2024 ‐ AI‐Powered Summarization and “Interaction” with Academic Papers
- GSoC 2022 — Implement a Three Way Merge UI for merging BibTeX entries
- GSoC 2021 - Improve pdf support in JabRef
- GSoC 2021 - Microsoft Word Integration
- GSoc 2019 - Bidirectional Integration — Paper Writing — LaTeX and JabRef 5.0
- GSoC Archive
- Release
- JabCon Archive