From a43b2f70a749dc688082dc89c4bf5fe4c8cb4aa6 Mon Sep 17 00:00:00 2001 From: Saoirse Aronson Date: Wed, 30 Sep 2026 11:49:51 +0200 Subject: [PATCH 1/4] added initial ruleset test case Signed-off-by: Saoirse Aronson Co-authored-by: Sasha Kobrusev --- .../eclipse/biscuit/token/AuthorizerTest.java | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java b/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java index 621c1baf..72d55922 100644 --- a/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java +++ b/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java @@ -5,7 +5,8 @@ package org.eclipse.biscuit.token; -import static org.eclipse.biscuit.token.builder.Utils.constrainedRule; +import static org.eclipse.biscuit.crypto.TokenSignature.hex; +import static org.eclipse.biscuit.token.builder.Utils.*; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -20,6 +21,9 @@ import org.eclipse.biscuit.datalog.RunLimits; import org.eclipse.biscuit.error.Error; import org.eclipse.biscuit.error.Error.Parser; +import org.eclipse.biscuit.error.FailedCheck; +import org.eclipse.biscuit.error.LogicError; +import org.eclipse.biscuit.token.builder.Block; import org.eclipse.biscuit.token.builder.Term; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -57,6 +61,78 @@ public void testAuthorizerPolicy() throws Parser { assertEquals(3, policies.size()); } + @Test + public void ruleSetHashIssue() throws Exception { + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + + org.eclipse.biscuit.token.builder.Block authorityBuilder = new org.eclipse.biscuit.token.builder.Block(); + + authorityBuilder.addFact(fact("right", Arrays.asList(str("file1"), str("read")))); + authorityBuilder.addFact(fact("right", Arrays.asList(str("file2"), str("read")))); + authorityBuilder.addFact(fact("right", Arrays.asList(str("file1"), str("write")))); + + Biscuit b = Biscuit.make(rng, root, authorityBuilder.build()); + + byte[] data = b.serialize(); + + Biscuit deser = Biscuit.fromBytes(data, root.getPublicKey()); + assertEquals(1, deser.blockCount()); + + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + + org.eclipse.biscuit.token.builder.Block builder = deser.createBlock(); + builder.addCheck( + check( + rule( + "caveat1", + List.of(var("resource")), + Arrays.asList( + pred("resource", List.of(var("resource"))), + pred("operation", List.of(str("read"))), + pred("right", Arrays.asList(var("resource"), str("read"))))))); + + builder.addRule("left($0, $1) <- right($0, $1)"); + builder.addRule("straight($0, $1) <- right($0, $1)"); + + Biscuit b2 = deser.attenuate(rng, keypair2, builder); + + byte[] data2 = b2.serialize(); + + Biscuit finalToken = Biscuit.fromBytes(data2, root.getPublicKey()); + assertEquals(2, finalToken.blockCount()); + + Authorizer authorizer = finalToken.authorizer(); + authorizer.addFact("resource(\"file1\")"); + authorizer.addFact("operation(\"read\")"); + authorizer.addPolicy("allow if true"); + authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + +// Authorizer authorizer2 = finalToken.authorizer(); +// authorizer2.addFact("resource(\"file2\")"); +// authorizer2.addFact("operation(\"write\")"); +// authorizer2.addPolicy("allow if true"); + +// try { +// authorizer2.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); +// } catch (Error e) { +// System.out.println(e); +// assertEquals( +// new Error.FailedLogic( +// new LogicError.Unauthorized( +// new LogicError.MatchedPolicy.Allow(0), +// Arrays.asList( +// new FailedCheck.FailedBlock( +// 1, +// 0, +// "check if resource($resource), operation(\"read\"), right($resource," +// + " \"read\")"), +// new FailedCheck.FailedBlock(2, 0, "check if resource(\"file1\")")))), +// e); +// } + + + } + @Test public void testPuttingSomeFactsInBiscuitAndGettingThemBackOutAgain() throws Exception { KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); From 9a79798037a0a94a5f54b78de43b76fe9dbc317c Mon Sep 17 00:00:00 2001 From: Saoirse Aronson Date: Wed, 30 Sep 2026 11:49:51 +0200 Subject: [PATCH 2/4] added world tests that showcase incorrect inclusion into world ruleset Signed-off-by: Saoirse Aronson Co-authored-by: Sasha Kobrusev --- .../eclipse/biscuit/datalog/WorldTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java index 844a0bef..c69b1e17 100644 --- a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java +++ b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java @@ -5,7 +5,9 @@ package org.eclipse.biscuit.datalog; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Instant; @@ -762,4 +764,68 @@ public void testResource() throws Error { } assertTrue(res.size() == 0); } + + @Test + public void testAddRuleSameTrustedOriginsInstanceThrows() { + final World w = new World(); + final SymbolTable syms = new SymbolTable(); + + final long left = syms.insert("left"); + final long straight = syms.insert("straight"); + final long right = syms.insert("right"); + final Term.Variable a = new Term.Variable(syms.insert("a")); + final Term.Variable b = new Term.Variable(syms.insert("b")); + + final Rule r1 = + new Rule( + new Predicate(left, Arrays.asList(a, b)), + Arrays.asList(new Predicate(right, Arrays.asList(a, b))), + new ArrayList<>()); + final Rule r2 = + new Rule( + new Predicate(straight, Arrays.asList(a, b)), + Arrays.asList(new Predicate(right, Arrays.asList(a, b))), + new ArrayList<>()); + + System.out.println("adding r1: " + syms.formatRule(r1)); + System.out.println("adding r2 under the SAME TrustedOrigins instance: " + syms.formatRule(r2)); + + final TrustedOrigins sharedScope = TrustedOrigins.defaultOrigins(); + w.addRule(1L, sharedScope, r1); + + assertThrows(UnsupportedOperationException.class, () -> w.addRule(1L, sharedScope, r2)); + } + + @Test + public void testAddRuleEqualButDistinctTrustedOriginsDoesNotThrowToday() { + final World w = new World(); + final SymbolTable syms = new SymbolTable(); + + final long left = syms.insert("left"); + final long straight = syms.insert("straight"); + final long right = syms.insert("right"); + final Term.Variable a = new Term.Variable(syms.insert("a")); + final Term.Variable b = new Term.Variable(syms.insert("b")); + + final Rule r1 = + new Rule( + new Predicate(left, Arrays.asList(a, b)), + Arrays.asList(new Predicate(right, Arrays.asList(a, b))), + new ArrayList<>()); + final Rule r2 = + new Rule( + new Predicate(straight, Arrays.asList(a, b)), + Arrays.asList(new Predicate(right, Arrays.asList(a, b))), + new ArrayList<>()); + + System.out.println("adding r1: " + syms.formatRule(r1)); + System.out.println( + "adding r2 under an equal but distinct TrustedOrigins instance: " + syms.formatRule(r2)); + + assertDoesNotThrow( + () -> { + w.addRule(1L, TrustedOrigins.defaultOrigins(), r1); + w.addRule(1L, TrustedOrigins.defaultOrigins(), r2); + }); + } } From 55edf7fe04a68cf090f10e30fe1de0de5ddd88e6 Mon Sep 17 00:00:00 2001 From: Saoirse Aronson Date: Wed, 30 Sep 2026 11:49:51 +0200 Subject: [PATCH 3/4] added fixes for correct hashing for trusted origin to allow it to be correctly used as key in ruleset Signed-off-by: Saoirse Aronson Co-authored-by: Sasha Kobrusev --- .../org/eclipse/biscuit/datalog/RuleSet.java | 2 +- .../biscuit/datalog/TrustedOrigins.java | 20 +++++++++++ .../eclipse/biscuit/datalog/WorldTest.java | 33 +------------------ 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/RuleSet.java b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/RuleSet.java index c311aee0..d4441913 100644 --- a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/RuleSet.java +++ b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/RuleSet.java @@ -20,7 +20,7 @@ public RuleSet() { public void add(Long origin, TrustedOrigins scope, Rule rule) { if (!rules.containsKey(scope)) { - rules.put(scope, List.of(new Pair<>(origin, rule))); + rules.put(scope, new ArrayList<>(List.of(new Pair<>(origin, rule)))); } else { rules.get(scope).add(new Pair<>(origin, rule)); } diff --git a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java index f5a74255..e0c279f6 100644 --- a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java +++ b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.List; +import java.util.Objects; public final class TrustedOrigins { private final Origin origin; @@ -91,6 +92,25 @@ public String toString() { return "TrustedOrigins{inner=" + origin + '}'; } + @Override + public boolean equals(Object o){ + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + TrustedOrigins trustedOrigin = (TrustedOrigins) o; + + return Objects.equals(origin, trustedOrigin.origin); + } + + @Override + public int hashCode() { + return origin.hashCode(); + } + public Origin getOrigin() { return origin; } diff --git a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java index c69b1e17..6f98f62f 100644 --- a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java +++ b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java @@ -766,38 +766,7 @@ public void testResource() throws Error { } @Test - public void testAddRuleSameTrustedOriginsInstanceThrows() { - final World w = new World(); - final SymbolTable syms = new SymbolTable(); - - final long left = syms.insert("left"); - final long straight = syms.insert("straight"); - final long right = syms.insert("right"); - final Term.Variable a = new Term.Variable(syms.insert("a")); - final Term.Variable b = new Term.Variable(syms.insert("b")); - - final Rule r1 = - new Rule( - new Predicate(left, Arrays.asList(a, b)), - Arrays.asList(new Predicate(right, Arrays.asList(a, b))), - new ArrayList<>()); - final Rule r2 = - new Rule( - new Predicate(straight, Arrays.asList(a, b)), - Arrays.asList(new Predicate(right, Arrays.asList(a, b))), - new ArrayList<>()); - - System.out.println("adding r1: " + syms.formatRule(r1)); - System.out.println("adding r2 under the SAME TrustedOrigins instance: " + syms.formatRule(r2)); - - final TrustedOrigins sharedScope = TrustedOrigins.defaultOrigins(); - w.addRule(1L, sharedScope, r1); - - assertThrows(UnsupportedOperationException.class, () -> w.addRule(1L, sharedScope, r2)); - } - - @Test - public void testAddRuleEqualButDistinctTrustedOriginsDoesNotThrowToday() { + public void testAddRuleEqualButDistinctTrustedOriginsDoesNotThrow() { final World w = new World(); final SymbolTable syms = new SymbolTable(); From b6dac7b6892da34f5f288bf697d2007af1137927 Mon Sep 17 00:00:00 2001 From: Saoirse Aronson Date: Wed, 30 Sep 2026 11:49:51 +0200 Subject: [PATCH 4/4] updated unit tests for ruleset issue, ran formatter restored not modified authorizerTest.java file remove unneeded authorizertest dependencies Signed-off-by: Saoirse Aronson Co-authored-by: Sasha Kobrusev --- .../biscuit/datalog/TrustedOrigins.java | 2 +- .../eclipse/biscuit/datalog/WorldTest.java | 2 +- .../eclipse/biscuit/token/AuthorizerTest.java | 78 +------------------ 3 files changed, 3 insertions(+), 79 deletions(-) diff --git a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java index e0c279f6..e6a4864d 100644 --- a/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java +++ b/biscuit-core/src/main/java/org/eclipse/biscuit/datalog/TrustedOrigins.java @@ -93,7 +93,7 @@ public String toString() { } @Override - public boolean equals(Object o){ + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java index 6f98f62f..354e5fbc 100644 --- a/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java +++ b/biscuit/src/test/java/org/eclipse/biscuit/datalog/WorldTest.java @@ -7,7 +7,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Instant; @@ -796,5 +795,6 @@ public void testAddRuleEqualButDistinctTrustedOriginsDoesNotThrow() { w.addRule(1L, TrustedOrigins.defaultOrigins(), r1); w.addRule(1L, TrustedOrigins.defaultOrigins(), r2); }); + assertEquals(1, w.getRules().getRules().size()); } } diff --git a/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java b/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java index 72d55922..621c1baf 100644 --- a/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java +++ b/biscuit/src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java @@ -5,8 +5,7 @@ package org.eclipse.biscuit.token; -import static org.eclipse.biscuit.crypto.TokenSignature.hex; -import static org.eclipse.biscuit.token.builder.Utils.*; +import static org.eclipse.biscuit.token.builder.Utils.constrainedRule; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -21,9 +20,6 @@ import org.eclipse.biscuit.datalog.RunLimits; import org.eclipse.biscuit.error.Error; import org.eclipse.biscuit.error.Error.Parser; -import org.eclipse.biscuit.error.FailedCheck; -import org.eclipse.biscuit.error.LogicError; -import org.eclipse.biscuit.token.builder.Block; import org.eclipse.biscuit.token.builder.Term; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,78 +57,6 @@ public void testAuthorizerPolicy() throws Parser { assertEquals(3, policies.size()); } - @Test - public void ruleSetHashIssue() throws Exception { - KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); - - org.eclipse.biscuit.token.builder.Block authorityBuilder = new org.eclipse.biscuit.token.builder.Block(); - - authorityBuilder.addFact(fact("right", Arrays.asList(str("file1"), str("read")))); - authorityBuilder.addFact(fact("right", Arrays.asList(str("file2"), str("read")))); - authorityBuilder.addFact(fact("right", Arrays.asList(str("file1"), str("write")))); - - Biscuit b = Biscuit.make(rng, root, authorityBuilder.build()); - - byte[] data = b.serialize(); - - Biscuit deser = Biscuit.fromBytes(data, root.getPublicKey()); - assertEquals(1, deser.blockCount()); - - KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); - - org.eclipse.biscuit.token.builder.Block builder = deser.createBlock(); - builder.addCheck( - check( - rule( - "caveat1", - List.of(var("resource")), - Arrays.asList( - pred("resource", List.of(var("resource"))), - pred("operation", List.of(str("read"))), - pred("right", Arrays.asList(var("resource"), str("read"))))))); - - builder.addRule("left($0, $1) <- right($0, $1)"); - builder.addRule("straight($0, $1) <- right($0, $1)"); - - Biscuit b2 = deser.attenuate(rng, keypair2, builder); - - byte[] data2 = b2.serialize(); - - Biscuit finalToken = Biscuit.fromBytes(data2, root.getPublicKey()); - assertEquals(2, finalToken.blockCount()); - - Authorizer authorizer = finalToken.authorizer(); - authorizer.addFact("resource(\"file1\")"); - authorizer.addFact("operation(\"read\")"); - authorizer.addPolicy("allow if true"); - authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); - -// Authorizer authorizer2 = finalToken.authorizer(); -// authorizer2.addFact("resource(\"file2\")"); -// authorizer2.addFact("operation(\"write\")"); -// authorizer2.addPolicy("allow if true"); - -// try { -// authorizer2.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); -// } catch (Error e) { -// System.out.println(e); -// assertEquals( -// new Error.FailedLogic( -// new LogicError.Unauthorized( -// new LogicError.MatchedPolicy.Allow(0), -// Arrays.asList( -// new FailedCheck.FailedBlock( -// 1, -// 0, -// "check if resource($resource), operation(\"read\"), right($resource," -// + " \"read\")"), -// new FailedCheck.FailedBlock(2, 0, "check if resource(\"file1\")")))), -// e); -// } - - - } - @Test public void testPuttingSomeFactsInBiscuitAndGettingThemBackOutAgain() throws Exception { KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);