Skip to content

allow using both white and blacklist at the same time with a configurable default response - #99

Merged
bamsemats merged 5 commits into
mainfrom
fix/79-whiteandblacklist
Feb 23, 2026
Merged

allow using both white and blacklist at the same time with a configurable default response#99
bamsemats merged 5 commits into
mainfrom
fix/79-whiteandblacklist

Conversation

@jesperlarsson1910

@jesperlarsson1910 jesperlarsson1910 commented Feb 19, 2026

Copy link
Copy Markdown

Added a field for default response and a new constructor to configure it, if the other constructor is used it is set to true.

Created a sub issue #98 to expand on this functionality but limiting this PR to the first issue.

Updated the tests to check the new logic and moved repeated fields out of the methods.

Summary by CodeRabbit

  • New Features

    • IP filter now supports configurable default access for unknown IPs, explicit whitelist/blacklist handling, and deterministic resolution when an IP appears in both lists.
  • Tests

    • Expanded coverage for allow/deny scenarios: whitelist-only, blacklist-only, both-lists with default-true, and neither-lists with default-false; tests now reuse shared fixtures.

…able default response

Signed-off-by: Jesper Larsson <jesper.larsson@iths.se>
@jesperlarsson1910 jesperlarsson1910 linked an issue Feb 19, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Feb 19, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jesperlarsson1910 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7b140bf and fd3799a.

📒 Files selected for processing (1)
  • src/test/java/org/juv25d/filter/IpFilterTest.java
📝 Walkthrough

Walkthrough

Refactors IpFilter to centralize access logic into a new public isAllowed(String) gate, adds internal whitelist/blacklist state and an allowByDefault flag with a new three-argument constructor, updates doFilter to delegate to isAllowed, adapts tests to the new constructor and shared fixtures, and updates App to call the new constructor.

Changes

Cohort / File(s) Summary
IP Filter Core Logic
src/main/java/org/juv25d/filter/IpFilter.java
Introduces internal whitelist/blacklist HashSets and allowByDefault field. Adds IpFilter(Set<String>, Set<String>, boolean) constructor and public boolean isAllowed(String) encapsulating allow/block logic. doFilter now delegates to isAllowed.
IP Filter Tests
src/test/java/org/juv25d/filter/IpFilterTest.java
Tests updated to use shared req, res, chain fixtures and the new three-arg IpFilter constructor. Adds/adjusts cases for IPs in both lists and for default-allow/default-block behaviors.
Application Pipeline
src/main/java/org/juv25d/App.java
Updates IpFilter instantiation at pipeline position to new IpFilter(Set.of(), Set.of(), true) to match the new constructor signature. No other functional changes observed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • Cavve

Poem

🐰 I hopped the code to guard the gate,
Whitelists stitched and blacklist slate,
A little bool decides the way,
Tests set course so none will stray,
Hooray — the filter knows who may play! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing simultaneous white/blacklist support with a configurable default response, which is the primary purpose of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/79-whiteandblacklist

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/main/java/org/juv25d/filter/IpFilter.java (2)

49-49: Consider narrowing isAllowed visibility to package-private.

isAllowed is an implementation detail of the filter; all existing tests reach it through doFilter. Declaring it public permanently widens the API contract of IpFilter.

♻️ Proposed change
-    public boolean isAllowed(String ip) {
+    boolean isAllowed(String ip) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/filter/IpFilter.java` at line 49, The method
isAllowed in class IpFilter is unnecessarily public; change its visibility to
package-private (remove the public modifier) so it becomes an implementation
detail, keep its signature otherwise, and ensure any external callers use
doFilter (tests already do), updating any direct calls to IpFilter.isAllowed to
go through doFilter or move tests into the same package if they must access it.

18-36: Delegate the 2-arg constructor to the 3-arg one to eliminate duplication.

Both constructors repeat identical null-check and addAll logic.

♻️ Proposed refactor
 public IpFilter(Set<String> whitelist, Set<String> blacklist) {
-    if (whitelist != null) {
-        this.whitelist.addAll(whitelist);
-    }
-    if (blacklist != null) {
-        this.blacklist.addAll(blacklist);
-    }
-    this.allowByDefault = true;
+    this(whitelist, blacklist, true);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/filter/IpFilter.java` around lines 18 - 36, The two
IpFilter constructors duplicate the same null-check and addAll logic; refactor
the 2-arg constructor to delegate to the 3-arg one by calling the three-argument
constructor (IpFilter(Set<String>, Set<String>, boolean)) with
allowByDefault=true so all initialization (whitelist.addAll, blacklist.addAll,
and allowByDefault assignment) is centralized in the single constructor and
duplication is removed.
src/test/java/org/juv25d/filter/IpFilterTest.java (2)

17-91: Minor naming inconsistency across test methods.

Test names mix Ip (camelCase) and IP (all-caps), and inBothList (singular) vs inBothLists (plural). Suggest picking one convention and applying it consistently — e.g. always Ip to stay idiomatic with Java's camelCase convention.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java` around lines 17 - 91,
Rename the test methods in IpFilterTest to use a consistent naming convention
(prefer Java camelCase with "Ip" and plural "Lists" where appropriate); for
example, rename allowsIP_inNeitherList_noChangeDefault ->
allowsIp_inNeitherLists_noChangeDefault, blocksIP_inBothList_ChangeDefault ->
blocksIp_inBothLists_changeDefault, blocksIP_inNeitherList_ChangeDefault ->
blocksIp_inNeitherLists_changeDefault, and adjust any references to these
methods in the class so all test names consistently use "Ip" (not "IP") and
"Lists" (not mixed singular/plural).

17-91: Two important coverage gaps after the removal of whitelist_blocksIpNotInList.

The test suite no longer covers:

  1. Whitelist provided, IP not in the list, default constructor — confirms the new allow-by-default semantic that replaces the old blocking behavior. Without this, the behavioral change from the 2-arg constructor is not explicitly documented in tests.
  2. Whitelist provided, IP not in the list, allowByDefault=false — the "strict whitelist" mode that existing callers may expect from the old constructor.
✅ Suggested additional test cases
`@Test`
void allowsIP_notInWhitelist_noChangeDefault() throws IOException {
    // 2-arg constructor: allowByDefault=true → IP not in whitelist should now be ALLOWED
    IpFilter filter = new IpFilter(Set.of("192.168.1.1"), null);

    when(req.remoteIp()).thenReturn("127.0.0.1");

    filter.doFilter(req, res, chain);

    verify(chain).doFilter(req, res);
    assertEquals(200, res.statusCode());
}

`@Test`
void blocksIP_notInWhitelist_ChangeDefault() throws IOException {
    // 3-arg constructor with allowByDefault=false: strict whitelist mode
    IpFilter filter = new IpFilter(Set.of("192.168.1.1"), null, false);

    when(req.remoteIp()).thenReturn("127.0.0.1");

    filter.doFilter(req, res, chain);
    verify(chain, never()).doFilter(req, res);

    assertEquals(403, res.statusCode());
    assertEquals("Forbidden", res.statusText());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java` around lines 17 - 91, The
test suite is missing coverage for the "IP not in whitelist" cases introduced by
the new IpFilter semantics; add two tests in IpFilterTest targeting the IpFilter
constructors: 1) a test (e.g., allowsIP_notInWhitelist_noChangeDefault) that
constructs IpFilter with new IpFilter(Set.of("192.168.1.1"), null) and asserts
the request is allowed (chain.doFilter called, statusCode 200) when
req.remoteIp() returns "127.0.0.1"; 2) a test (e.g.,
blocksIP_notInWhitelist_ChangeDefault) that constructs IpFilter with new
IpFilter(Set.of("192.168.1.1"), null, false) and asserts the request is blocked
(chain.doFilter never called, statusCode 403 and statusText "Forbidden") when
req.remoteIp() returns "127.0.0.1". Ensure both tests use the same mocked
req/res/chain setup as other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/juv25d/filter/IpFilter.java`:
- Around line 18-26: The two-arg constructor IpFilter(Set<String> whitelist,
Set<String> blacklist) currently sets allowByDefault = true causing a breaking
semantic change; revert to the original strict-whitelist default by delegating
to or setting the three-arg behavior: call or mimic the three-arg constructor
(IpFilter(Set<String>, Set<String>, boolean)) with allowByDefault = false (or
explicitly set this.allowByDefault = false) so that IPs not in either list are
denied unless the caller opts into permissive behavior; update the constructor
body that manipulates whitelist/blacklist to reuse the same initialization logic
as the 3-arg constructor (or delegate to it) to avoid duplication.

---

Nitpick comments:
In `@src/main/java/org/juv25d/filter/IpFilter.java`:
- Line 49: The method isAllowed in class IpFilter is unnecessarily public;
change its visibility to package-private (remove the public modifier) so it
becomes an implementation detail, keep its signature otherwise, and ensure any
external callers use doFilter (tests already do), updating any direct calls to
IpFilter.isAllowed to go through doFilter or move tests into the same package if
they must access it.
- Around line 18-36: The two IpFilter constructors duplicate the same null-check
and addAll logic; refactor the 2-arg constructor to delegate to the 3-arg one by
calling the three-argument constructor (IpFilter(Set<String>, Set<String>,
boolean)) with allowByDefault=true so all initialization (whitelist.addAll,
blacklist.addAll, and allowByDefault assignment) is centralized in the single
constructor and duplication is removed.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java`:
- Around line 17-91: Rename the test methods in IpFilterTest to use a consistent
naming convention (prefer Java camelCase with "Ip" and plural "Lists" where
appropriate); for example, rename allowsIP_inNeitherList_noChangeDefault ->
allowsIp_inNeitherLists_noChangeDefault, blocksIP_inBothList_ChangeDefault ->
blocksIp_inBothLists_changeDefault, blocksIP_inNeitherList_ChangeDefault ->
blocksIp_inNeitherLists_changeDefault, and adjust any references to these
methods in the class so all test names consistently use "Ip" (not "IP") and
"Lists" (not mixed singular/plural).
- Around line 17-91: The test suite is missing coverage for the "IP not in
whitelist" cases introduced by the new IpFilter semantics; add two tests in
IpFilterTest targeting the IpFilter constructors: 1) a test (e.g.,
allowsIP_notInWhitelist_noChangeDefault) that constructs IpFilter with new
IpFilter(Set.of("192.168.1.1"), null) and asserts the request is allowed
(chain.doFilter called, statusCode 200) when req.remoteIp() returns "127.0.0.1";
2) a test (e.g., blocksIP_notInWhitelist_ChangeDefault) that constructs IpFilter
with new IpFilter(Set.of("192.168.1.1"), null, false) and asserts the request is
blocked (chain.doFilter never called, statusCode 403 and statusText "Forbidden")
when req.remoteIp() returns "127.0.0.1". Ensure both tests use the same mocked
req/res/chain setup as other tests.

Comment thread src/main/java/org/juv25d/filter/IpFilter.java Outdated
SandraNelj
SandraNelj previously approved these changes Feb 19, 2026

@SandraNelj SandraNelj left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks good! The ability to configure default behavior for whitelist/blacklist makes the filter more flexible. The updated tests also help verify the behavior clearly!

Signed-off-by: Jesper Larsson <jesper.larsson@iths.se>
bamsemats
bamsemats previously approved these changes Feb 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/juv25d/App.java (1)

29-29: ⚠️ Potential issue | 🔴 Critical

Stale 2-arg IpFilter constructor call will fail to compile after the conflict is resolved.

The 2-arg IpFilter(Set, Set) constructor was removed in this PR (only the 3-arg form exists now). Line 29 must be deleted — its role is superseded by the 3-arg instantiation inside the conflict block (lines 44–48).

🐛 Proposed fix — remove the stale registration
-        pipeline.addGlobalFilter(new IpFilter(Set.of(), Set.of()), 2);
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/App.java` at line 29, Remove the stale 2-arg
registration call to IpFilter by deleting the line invoking
pipeline.addGlobalFilter(new IpFilter(Set.of(), Set.of()), 2); because the
codebase now only provides the 3-arg IpFilter constructor and the filter is
already registered in the conflict block via the 3-arg instantiation (lines
around the conflict). Ensure only the 3-arg IpFilter(...) registration remains
and no other references to the removed 2-arg constructor are left.
🧹 Nitpick comments (2)
src/test/java/org/juv25d/filter/IpFilterTest.java (1)

43-66: Good new coverage; add the symmetric allowByDefault counterparts.

The two new tests cover (both-lists, default=true) and (neither-list, default=false). The symmetric cases — (both-lists, default=false)blocked, and (neither-list, default=true)allowed — are equally non-obvious and currently untested.

✅ Suggested additional tests
`@Test`
void blocksIP_inBothList_defaultFalse() throws IOException {
    IpFilter filter = new IpFilter(Set.of("127.0.0.1"), Set.of("127.0.0.1"), false);

    when(req.remoteIp()).thenReturn("127.0.0.1");

    filter.doFilter(req, res, chain);

    verify(chain, never()).doFilter(req, res);
    assertEquals(403, res.statusCode());
}

`@Test`
void allowsIP_inNeitherList_defaultTrue() throws IOException {
    IpFilter filter = new IpFilter(null, null, true);

    when(req.remoteIp()).thenReturn("127.0.0.1");

    filter.doFilter(req, res, chain);

    verify(chain).doFilter(req, res);
    assertEquals(200, res.statusCode());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java` around lines 43 - 66, Add
two symmetric unit tests for IpFilter to cover the missing allowByDefault
variants: create a test method blocksIP_inBothList_defaultFalse that constructs
new IpFilter(Set.of("127.0.0.1"), Set.of("127.0.0.1"), false), stubs
req.remoteIp() to "127.0.0.1", invokes filter.doFilter(req, res, chain), then
verify(chain, never()).doFilter(req, res) and assert res.statusCode() is 403;
and create allowsIP_inNeitherList_defaultTrue that constructs new IpFilter(null,
null, true), stubs req.remoteIp(), invokes doFilter, then
verify(chain).doFilter(req, res) and assert res.statusCode() is 200.
src/main/java/org/juv25d/filter/IpFilter.java (1)

41-50: isAllowed logic is correct; consider documenting null-IP behavior.

The four-case precedence (both → allowByDefault, whitelist-only → true, blacklist-only → false, neither → allowByDefault) is well-defined and aligns with the tests. One edge case worth a brief comment: if getClientIp ever returns null (e.g., behind a proxy), HashSet.contains(null) returns false, so the call quietly falls through to allowByDefault. This is safe but silent.

💡 Optional: guard or document the null-IP fallback
 public boolean isAllowed(String ip) {
+    // null IP (e.g., missing X-Forwarded-For) defers to allowByDefault
     if (whitelist.contains(ip) && blacklist.contains(ip)) return allowByDefault;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/filter/IpFilter.java` around lines 41 - 50, The
isAllowed method's behavior when ip is null should be made explicit: inside
isAllowed(String ip) (which currently checks whitelist, blacklist and
allowByDefault), either add an early null check (e.g., if (ip == null) return
allowByDefault) or add a concise comment above the method documenting that
getClientIp may return null and that contains(null) will be treated as "neither"
and thus fall back to allowByDefault; update the isAllowed method or its javadoc
accordingly and reference whitelist, blacklist and allowByDefault in the
comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/juv25d/App.java`:
- Around line 39-50: Remove the raw merge conflict markers and the duplicated
RedirectFilter registration: delete the conflict blocks (<<<<<<<, =======,
>>>>>>>) and the extra pipeline.addGlobalFilter(new
RedirectFilter(redirectRules), 0) introduced by the fix branch, leaving only the
pipeline.addGlobalFilter(new IpFilter(Set.of(), Set.of(), true), 0) call; ensure
the remaining pipeline.addGlobalFilter usages (including the existing
RedirectFilter at position 4) remain intact and compile.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java`:
- Line 45: The test contains a stray double semicolon after constructing the
IpFilter instance in IpFilterTest; remove the extra semicolon so the line using
new IpFilter(Set.of("127.0.0.1"), Set.of("127.0.0.1"), true) ends with a single
semicolon to fix the syntax error.

---

Outside diff comments:
In `@src/main/java/org/juv25d/App.java`:
- Line 29: Remove the stale 2-arg registration call to IpFilter by deleting the
line invoking pipeline.addGlobalFilter(new IpFilter(Set.of(), Set.of()), 2);
because the codebase now only provides the 3-arg IpFilter constructor and the
filter is already registered in the conflict block via the 3-arg instantiation
(lines around the conflict). Ensure only the 3-arg IpFilter(...) registration
remains and no other references to the removed 2-arg constructor are left.

---

Nitpick comments:
In `@src/main/java/org/juv25d/filter/IpFilter.java`:
- Around line 41-50: The isAllowed method's behavior when ip is null should be
made explicit: inside isAllowed(String ip) (which currently checks whitelist,
blacklist and allowByDefault), either add an early null check (e.g., if (ip ==
null) return allowByDefault) or add a concise comment above the method
documenting that getClientIp may return null and that contains(null) will be
treated as "neither" and thus fall back to allowByDefault; update the isAllowed
method or its javadoc accordingly and reference whitelist, blacklist and
allowByDefault in the comment.

In `@src/test/java/org/juv25d/filter/IpFilterTest.java`:
- Around line 43-66: Add two symmetric unit tests for IpFilter to cover the
missing allowByDefault variants: create a test method
blocksIP_inBothList_defaultFalse that constructs new
IpFilter(Set.of("127.0.0.1"), Set.of("127.0.0.1"), false), stubs req.remoteIp()
to "127.0.0.1", invokes filter.doFilter(req, res, chain), then verify(chain,
never()).doFilter(req, res) and assert res.statusCode() is 403; and create
allowsIP_inNeitherList_defaultTrue that constructs new IpFilter(null, null,
true), stubs req.remoteIp(), invokes doFilter, then verify(chain).doFilter(req,
res) and assert res.statusCode() is 200.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da051fd and 3ccdb57.

📒 Files selected for processing (3)
  • src/main/java/org/juv25d/App.java
  • src/main/java/org/juv25d/filter/IpFilter.java
  • src/test/java/org/juv25d/filter/IpFilterTest.java

Comment thread src/main/java/org/juv25d/App.java Outdated
Comment thread src/test/java/org/juv25d/filter/IpFilterTest.java Outdated
Signed-off-by: Jesper Larsson <jesper.larsson@iths.se>
Signed-off-by: Jesper Larsson <jesper.larsson@iths.se>
Comment thread src/main/java/org/juv25d/filter/IpFilter.java
@bamsemats
bamsemats merged commit bd127df into main Feb 23, 2026
2 checks passed
@jesperlarsson1910
jesperlarsson1910 deleted the fix/79-whiteandblacklist branch February 23, 2026 14:30
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.

Allow to filter with white and blacklist simultaneous

4 participants