Skip to content

Commit e47fc6f

Browse files
coopernetesclaude
andcommitted
feat: unified filter/hook order ranges; add GitProxyHook interface and OrderableGitProxyFilter
Introduces a unified order numbering scheme across both proxy modes so operators can reason about execution order from a single table: System MIN_VALUE … MIN_VALUE+99 (ForceGitClient, ParseGitRequest, EnrichPushCommits) Authorization 0 … 199 (AllowApproved 50, Whitelist 100, UserPermission 150) Content 200 … 399 (EmptyBranch 210, HiddenCommits 220, AuthorEmail 250, CommitMessage 260, ScanDiff 300, Gpg 320, SecretScan 340) Extended 400 … 499 (reserved for user-defined filters) Terminal MAX_VALUE-3 … MAX_VALUE-1 (ValidationSummary, FetchFinalizer, PushFinalizer) Audit MAX_VALUE (AuditLog) Store-and-forward hooks now implement GitProxyHook (getOrder()) and are sorted by order in StoreAndForwardReceivePackFactory rather than relying on list insertion order. Transparent-proxy filters are collected into a List, sorted by getOrder(), then added to Jetty — so the constant alone controls execution order regardless of registration sequence. OrderableGitProxyFilter extends GitProxyFilter with setOrder(int) for user-supplied filters that need to be positioned at runtime. Closes #13 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 978d09d commit e47fc6f

32 files changed

Lines changed: 345 additions & 238 deletions

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/AuthorEmailValidationHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import lombok.extern.slf4j.Slf4j;
88
import org.eclipse.jgit.lib.ObjectId;
99
import org.eclipse.jgit.lib.Repository;
10-
import org.eclipse.jgit.transport.PreReceiveHook;
1110
import org.eclipse.jgit.transport.ReceiveCommand;
1211
import org.eclipse.jgit.transport.ReceivePack;
1312
import org.finos.gitproxy.config.CommitConfig;
@@ -22,9 +21,9 @@
2221
*/
2322
@Slf4j
2423
@RequiredArgsConstructor
25-
public class AuthorEmailValidationHook implements PreReceiveHook {
24+
public class AuthorEmailValidationHook implements GitProxyHook {
2625

27-
private static final int STEP_ORDER = 2100;
26+
private static final int ORDER = 250;
2827

2928
private final CommitConfig commitConfig;
3029
private final ValidationContext validationContext;
@@ -52,12 +51,22 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
5251
if (allViolations.isEmpty()) {
5352
pushContext.addStep(PushStep.builder()
5453
.stepName("checkAuthorEmails")
55-
.stepOrder(STEP_ORDER)
54+
.stepOrder(ORDER)
5655
.status(StepStatus.PASS)
5756
.build());
5857
}
5958
}
6059

60+
@Override
61+
public int getOrder() {
62+
return ORDER;
63+
}
64+
65+
@Override
66+
public String getName() {
67+
return "AuthorEmailValidationHook";
68+
}
69+
6170
private List<Commit> getCommits(Repository repo, ReceiveCommand cmd) throws Exception {
6271
if (ObjectId.zeroId().equals(cmd.getOldId())) {
6372
return List.of(CommitInspectionService.getCommitDetails(

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/CheckEmptyBranchHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import lombok.extern.slf4j.Slf4j;
1212
import org.eclipse.jgit.lib.ObjectId;
1313
import org.eclipse.jgit.lib.Repository;
14-
import org.eclipse.jgit.transport.PreReceiveHook;
1514
import org.eclipse.jgit.transport.ReceiveCommand;
1615
import org.eclipse.jgit.transport.ReceivePack;
1716
import org.finos.gitproxy.db.model.PushStep;
@@ -31,9 +30,9 @@
3130
*/
3231
@Slf4j
3332
@RequiredArgsConstructor
34-
public class CheckEmptyBranchHook implements PreReceiveHook {
33+
public class CheckEmptyBranchHook implements GitProxyHook {
3534

36-
private static final int STEP_ORDER = 2050;
35+
private static final int ORDER = 210;
3736
private static final String STEP_NAME = "checkEmptyBranch";
3837

3938
private final PushContext pushContext;
@@ -69,12 +68,22 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
6968
if (pushContext != null) {
7069
pushContext.addStep(PushStep.builder()
7170
.stepName(STEP_NAME)
72-
.stepOrder(STEP_ORDER)
71+
.stepOrder(ORDER)
7372
.status(StepStatus.PASS)
7473
.build());
7574
}
7675
}
7776

77+
@Override
78+
public int getOrder() {
79+
return ORDER;
80+
}
81+
82+
@Override
83+
public String getName() {
84+
return "CheckEmptyBranchHook";
85+
}
86+
7887
private List<Commit> getCommits(Repository repo, ReceiveCommand cmd) throws Exception {
7988
return CommitInspectionService.getCommitRange(
8089
repo, cmd.getOldId().name(), cmd.getNewId().name());

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/CheckHiddenCommitsHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.eclipse.jgit.lib.Repository;
1717
import org.eclipse.jgit.revwalk.RevCommit;
1818
import org.eclipse.jgit.revwalk.RevWalk;
19-
import org.eclipse.jgit.transport.PreReceiveHook;
2019
import org.eclipse.jgit.transport.ReceiveCommand;
2120
import org.eclipse.jgit.transport.ReceivePack;
2221
import org.finos.gitproxy.db.model.PushStep;
@@ -41,9 +40,9 @@
4140
*/
4241
@Slf4j
4342
@RequiredArgsConstructor
44-
public class CheckHiddenCommitsHook implements PreReceiveHook {
43+
public class CheckHiddenCommitsHook implements GitProxyHook {
4544

46-
private static final int STEP_ORDER = 2060;
45+
private static final int ORDER = 220;
4746
private static final String STEP_NAME = "checkHiddenCommits";
4847

4948
private final PushContext pushContext;
@@ -63,7 +62,7 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
6362
if (pushContext != null) {
6463
pushContext.addStep(PushStep.builder()
6564
.stepName(STEP_NAME)
66-
.stepOrder(STEP_ORDER)
65+
.stepOrder(ORDER)
6766
.status(StepStatus.PASS)
6867
.build());
6968
}
@@ -90,6 +89,16 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
9089
}
9190
}
9291

92+
@Override
93+
public int getOrder() {
94+
return ORDER;
95+
}
96+
97+
@Override
98+
public String getName() {
99+
return "CheckHiddenCommitsHook";
100+
}
101+
93102
private Set<String> collectIntroducedCommits(Repository repo, Collection<ReceiveCommand> commands)
94103
throws Exception {
95104
Set<String> introduced = new HashSet<>();

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/CheckUserPushPermissionHook.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.Collection;
99
import lombok.RequiredArgsConstructor;
1010
import lombok.extern.slf4j.Slf4j;
11-
import org.eclipse.jgit.transport.PreReceiveHook;
1211
import org.eclipse.jgit.transport.ReceiveCommand;
1312
import org.eclipse.jgit.transport.ReceivePack;
1413
import org.finos.gitproxy.db.model.PushStep;
@@ -24,9 +23,9 @@
2423
*/
2524
@Slf4j
2625
@RequiredArgsConstructor
27-
public class CheckUserPushPermissionHook implements PreReceiveHook {
26+
public class CheckUserPushPermissionHook implements GitProxyHook {
2827

29-
private static final int STEP_ORDER = 2000;
28+
private static final int ORDER = 150;
3029

3130
private final UserAuthorizationService userAuthorizationService;
3231
private final ValidationContext validationContext;
@@ -40,7 +39,7 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
4039
log.debug("No push user found in repo config, skipping permission check");
4140
pushContext.addStep(PushStep.builder()
4241
.stepName("checkUserPermission")
43-
.stepOrder(STEP_ORDER)
42+
.stepOrder(ORDER)
4443
.status(StepStatus.PASS)
4544
.build());
4645
return;
@@ -73,8 +72,18 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
7372
log.debug("Push user {} is authorized", pushUser);
7473
pushContext.addStep(PushStep.builder()
7574
.stepName("checkUserPermission")
76-
.stepOrder(STEP_ORDER)
75+
.stepOrder(ORDER)
7776
.status(StepStatus.PASS)
7877
.build());
7978
}
79+
80+
@Override
81+
public int getOrder() {
82+
return ORDER;
83+
}
84+
85+
@Override
86+
public String getName() {
87+
return "CheckUserPushPermissionHook";
88+
}
8089
}

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/CommitMessageValidationHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import lombok.extern.slf4j.Slf4j;
88
import org.eclipse.jgit.lib.ObjectId;
99
import org.eclipse.jgit.lib.Repository;
10-
import org.eclipse.jgit.transport.PreReceiveHook;
1110
import org.eclipse.jgit.transport.ReceiveCommand;
1211
import org.eclipse.jgit.transport.ReceivePack;
1312
import org.finos.gitproxy.config.CommitConfig;
@@ -22,9 +21,9 @@
2221
*/
2322
@Slf4j
2423
@RequiredArgsConstructor
25-
public class CommitMessageValidationHook implements PreReceiveHook {
24+
public class CommitMessageValidationHook implements GitProxyHook {
2625

27-
private static final int STEP_ORDER = 2200;
26+
private static final int ORDER = 260;
2827

2928
private final CommitConfig commitConfig;
3029
private final ValidationContext validationContext;
@@ -52,12 +51,22 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
5251
if (allViolations.isEmpty()) {
5352
pushContext.addStep(PushStep.builder()
5453
.stepName("checkCommitMessages")
55-
.stepOrder(STEP_ORDER)
54+
.stepOrder(ORDER)
5655
.status(StepStatus.PASS)
5756
.build());
5857
}
5958
}
6059

60+
@Override
61+
public int getOrder() {
62+
return ORDER;
63+
}
64+
65+
@Override
66+
public String getName() {
67+
return "CommitMessageValidationHook";
68+
}
69+
6170
private List<Commit> getCommits(Repository repo, ReceiveCommand cmd) throws Exception {
6271
if (ObjectId.zeroId().equals(cmd.getOldId())) {
6372
return List.of(CommitInspectionService.getCommitDetails(

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/DiffGenerationHook.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import org.eclipse.jgit.lib.ObjectId;
88
import org.eclipse.jgit.lib.Ref;
99
import org.eclipse.jgit.lib.Repository;
10-
import org.eclipse.jgit.transport.PreReceiveHook;
1110
import org.eclipse.jgit.transport.ReceiveCommand;
1211
import org.eclipse.jgit.transport.ReceivePack;
1312
import org.finos.gitproxy.db.model.PushStep;
@@ -32,7 +31,9 @@
3231
*/
3332
@Slf4j
3433
@RequiredArgsConstructor
35-
public class DiffGenerationHook implements PreReceiveHook {
34+
public class DiffGenerationHook implements GitProxyHook {
35+
36+
private static final int ORDER = 280;
3637

3738
public static final String STEP_NAME_PUSH_DIFF = "diff";
3839
public static final String STEP_NAME_BRANCH_DIFF = "diff:default-branch";
@@ -72,7 +73,7 @@ private void generatePushDiff(
7273

7374
PushStep step = PushStep.builder()
7475
.stepName(STEP_NAME_PUSH_DIFF)
75-
.stepOrder(3000)
76+
.stepOrder(ORDER)
7677
.status(StepStatus.PASS)
7778
.content(diff)
7879
.build();
@@ -118,7 +119,7 @@ private void generateDefaultBranchDiff(ReceivePack rp, Repository repo, String p
118119

119120
PushStep step = PushStep.builder()
120121
.stepName(STEP_NAME_BRANCH_DIFF)
121-
.stepOrder(3001)
122+
.stepOrder(ORDER + 1)
122123
.status(StepStatus.PASS)
123124
.content(diff)
124125
.build();
@@ -133,6 +134,16 @@ private void generateDefaultBranchDiff(ReceivePack rp, Repository repo, String p
133134
}
134135
}
135136

137+
@Override
138+
public int getOrder() {
139+
return ORDER;
140+
}
141+
142+
@Override
143+
public String getName() {
144+
return "DiffGenerationHook";
145+
}
146+
136147
/**
137148
* Resolve the default branch. In a bare clone, HEAD is a symbolic ref pointing to the remote's default branch.
138149
* Falls back to common names if HEAD is detached.

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/DiffScanningHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.List;
66
import lombok.RequiredArgsConstructor;
77
import lombok.extern.slf4j.Slf4j;
8-
import org.eclipse.jgit.transport.PreReceiveHook;
98
import org.eclipse.jgit.transport.ReceiveCommand;
109
import org.eclipse.jgit.transport.ReceivePack;
1110
import org.finos.gitproxy.config.CommitConfig;
@@ -23,9 +22,9 @@
2322
*/
2423
@Slf4j
2524
@RequiredArgsConstructor
26-
public class DiffScanningHook implements PreReceiveHook {
25+
public class DiffScanningHook implements GitProxyHook {
2726

28-
private static final int STEP_ORDER = 2300;
27+
private static final int ORDER = 300;
2928

3029
private final CommitConfig commitConfig;
3130
private final ValidationContext validationContext;
@@ -80,10 +79,20 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
8079
if (!anyFailed) {
8180
pushContext.addStep(PushStep.builder()
8281
.stepName("scanDiff")
83-
.stepOrder(STEP_ORDER)
82+
.stepOrder(ORDER)
8483
.status(StepStatus.PASS)
8584
.logs(logs)
8685
.build());
8786
}
8887
}
88+
89+
@Override
90+
public int getOrder() {
91+
return ORDER;
92+
}
93+
94+
@Override
95+
public String getName() {
96+
return "DiffScanningHook";
97+
}
8998
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.finos.gitproxy.git;
2+
3+
import org.eclipse.jgit.transport.PreReceiveHook;
4+
5+
/**
6+
* A {@link PreReceiveHook} with an associated order value, used to sort hooks in the store-and-forward receive chain.
7+
*
8+
* <p>Hooks are executed in ascending order of their {@link #getOrder()} value. The following ranges mirror the filter
9+
* order scheme and are reserved:
10+
*
11+
* <ul>
12+
* <li><b>Negative (&lt;= -1):</b> System lifecycle hooks that must run first or last (e.g., persistence hooks).
13+
* Custom hooks must not use negative values.
14+
* <li><b>0-199 authorization range:</b> Whitelist and user/repo/provider permission checks.
15+
* <li><b>200-399 content filtering range:</b> Commit content validation (emails, messages, diffs, signatures,
16+
* secrets). Built-in hooks use multiples of 10 within this range to leave room for custom hooks between them.
17+
* <li><b>400-499 post-validation range:</b> Reserved for future use (e.g., outbound commit decoration).
18+
* <li><b>500+ extended range:</b> Custom bespoke hooks.
19+
* </ul>
20+
*
21+
* <p>Lifecycle hooks ({@code PushStorePersistenceHook}, {@code ApprovalPreReceiveHook}) do not implement this interface
22+
* and are always pinned at fixed positions in the chain by {@link StoreAndForwardReceivePackFactory}.
23+
*/
24+
public interface GitProxyHook extends PreReceiveHook {
25+
26+
/** Returns the order value that determines this hook's position in the chain. */
27+
int getOrder();
28+
29+
/** Returns a human-readable name for this hook, used in logging and diagnostics. */
30+
String getName();
31+
}

jgit-proxy-core/src/main/java/org/finos/gitproxy/git/GpgSignatureHook.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import lombok.extern.slf4j.Slf4j;
1313
import org.eclipse.jgit.lib.ObjectId;
1414
import org.eclipse.jgit.lib.Repository;
15-
import org.eclipse.jgit.transport.PreReceiveHook;
1615
import org.eclipse.jgit.transport.ReceiveCommand;
1716
import org.eclipse.jgit.transport.ReceivePack;
1817
import org.finos.gitproxy.config.GpgConfig;
@@ -27,9 +26,9 @@
2726
*/
2827
@Slf4j
2928
@RequiredArgsConstructor
30-
public class GpgSignatureHook implements PreReceiveHook {
29+
public class GpgSignatureHook implements GitProxyHook {
3130

32-
private static final int STEP_ORDER = 2400;
31+
private static final int ORDER = 320;
3332

3433
private final GpgConfig config;
3534
private final ValidationContext validationContext;
@@ -59,12 +58,22 @@ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
5958
if (allViolations.isEmpty()) {
6059
pushContext.addStep(PushStep.builder()
6160
.stepName("checkSignatures")
62-
.stepOrder(STEP_ORDER)
61+
.stepOrder(ORDER)
6362
.status(StepStatus.PASS)
6463
.build());
6564
}
6665
}
6766

67+
@Override
68+
public int getOrder() {
69+
return ORDER;
70+
}
71+
72+
@Override
73+
public String getName() {
74+
return "GpgSignatureHook";
75+
}
76+
6877
private List<Commit> getCommits(Repository repo, ReceiveCommand cmd) throws Exception {
6978
if (ObjectId.zeroId().equals(cmd.getOldId())) {
7079
return List.of(CommitInspectionService.getCommitDetails(

0 commit comments

Comments
 (0)