Skip to content

Commit 6b87297

Browse files
Copilotcoopernetes
andcommitted
Add service layer and new filter implementations for commit validation and security scanning
Co-authored-by: coopernetes <57812123+coopernetes@users.noreply.github.com>
1 parent 0d25535 commit 6b87297

14 files changed

Lines changed: 1075 additions & 0 deletions

jgit-proxy-core/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ dependencies {
3838
// Jakarta annotations
3939
api "jakarta.annotation:jakarta.annotation-api:3.0.0"
4040

41+
// Apache Commons Validator for email validation
42+
implementation "commons-validator:commons-validator:1.9.0"
43+
44+
// BouncyCastle for GPG signature verification
45+
implementation "org.bouncycastle:bcprov-jdk18on:1.79"
46+
implementation "org.bouncycastle:bcpg-jdk18on:1.79"
47+
4148
// JGit dependencies
4249
api "org.eclipse.jgit:org.eclipse.jgit:${jgitVersion}"
4350
implementation "org.eclipse.jgit:org.eclipse.jgit.http.apache:${jgitVersion}"
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package org.finos.gitproxy.config;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
8+
/** Configuration for commit validation filters. */
9+
@Data
10+
@Builder
11+
public class CommitConfig {
12+
13+
/** Configuration for author email validation. */
14+
@Builder.Default
15+
private AuthorConfig author = AuthorConfig.builder().build();
16+
17+
/** Configuration for commit message validation. */
18+
@Builder.Default
19+
private MessageConfig message = MessageConfig.builder().build();
20+
21+
/** Configuration for author validation. */
22+
@Data
23+
@Builder
24+
public static class AuthorConfig {
25+
26+
/** Configuration for email validation. */
27+
@Builder.Default
28+
private EmailConfig email = EmailConfig.builder().build();
29+
}
30+
31+
/** Configuration for email validation. */
32+
@Data
33+
@Builder
34+
public static class EmailConfig {
35+
36+
/** Configuration for email domain validation. */
37+
@Builder.Default
38+
private DomainConfig domain = DomainConfig.builder().build();
39+
40+
/** Configuration for email local part validation. */
41+
@Builder.Default
42+
private LocalConfig local = LocalConfig.builder().build();
43+
}
44+
45+
/** Configuration for email domain validation. */
46+
@Data
47+
@Builder
48+
public static class DomainConfig {
49+
50+
/**
51+
* Regex pattern for allowed email domains. If set, only emails matching this pattern are allowed. Example:
52+
* ".*\\.company\\.com$" to allow only company.com domains.
53+
*/
54+
private String allow;
55+
}
56+
57+
/** Configuration for email local part validation. */
58+
@Data
59+
@Builder
60+
public static class LocalConfig {
61+
62+
/**
63+
* Regex pattern for blocked email local parts. If set, emails matching this pattern are blocked. Example:
64+
* "^(noreply|no-reply)$" to block noreply addresses.
65+
*/
66+
private String block;
67+
}
68+
69+
/** Configuration for commit message validation. */
70+
@Data
71+
@Builder
72+
public static class MessageConfig {
73+
74+
/** Configuration for blocking specific message patterns. */
75+
@Builder.Default
76+
private BlockConfig block = BlockConfig.builder().build();
77+
}
78+
79+
/** Configuration for blocking specific message patterns. */
80+
@Data
81+
@Builder
82+
public static class BlockConfig {
83+
84+
/**
85+
* List of literal strings that are blocked in commit messages. Messages containing any of these strings will be
86+
* rejected (case-insensitive).
87+
*/
88+
@Builder.Default
89+
private List<String> literals = new ArrayList<>();
90+
91+
/**
92+
* List of regex patterns that are blocked in commit messages. Messages matching any of these patterns will be
93+
* rejected.
94+
*/
95+
@Builder.Default
96+
private List<String> patterns = new ArrayList<>();
97+
}
98+
99+
/**
100+
* Create a default configuration with no restrictions.
101+
*
102+
* @return A default CommitConfig instance
103+
*/
104+
public static CommitConfig defaultConfig() {
105+
return CommitConfig.builder().build();
106+
}
107+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.finos.gitproxy.config;
2+
3+
import lombok.Builder;
4+
import lombok.Data;
5+
6+
/** Configuration for GPG signature validation. */
7+
@Data
8+
@Builder
9+
public class GpgConfig {
10+
11+
/** Whether GPG signature validation is enabled. */
12+
@Builder.Default
13+
private boolean enabled = false;
14+
15+
/** Whether to require all commits to be signed. */
16+
@Builder.Default
17+
private boolean requireSignedCommits = false;
18+
19+
/**
20+
* Path to a file containing trusted public keys in ASCII-armored format. If not set, a default dummy key will be
21+
* used for testing.
22+
*/
23+
private String trustedKeysFile;
24+
25+
/** Inline trusted public keys in ASCII-armored format. Used as an alternative to trustedKeysFile. */
26+
private String trustedKeysInline;
27+
28+
/**
29+
* Create a default configuration with GPG validation disabled.
30+
*
31+
* @return A default GpgConfig instance
32+
*/
33+
public static GpgConfig defaultConfig() {
34+
return GpgConfig.builder().build();
35+
}
36+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package org.finos.gitproxy.config;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
8+
/** Configuration for secret scanning filters. */
9+
@Data
10+
@Builder
11+
public class SecretScanningConfig {
12+
13+
/** Whether secret scanning is enabled. */
14+
@Builder.Default
15+
private boolean enabled = true;
16+
17+
/**
18+
* List of regex patterns to detect secrets. Each pattern should match potential secrets like API keys, passwords,
19+
* tokens, etc.
20+
*/
21+
@Builder.Default
22+
private List<String> patterns = new ArrayList<>();
23+
24+
/** Maximum file size to scan in bytes. Files larger than this will be skipped. */
25+
@Builder.Default
26+
private long maxFileSizeBytes = 1024 * 1024; // 1MB default
27+
28+
/**
29+
* List of file extensions to skip during scanning (e.g., ".jpg", ".png", ".zip"). Binary files are typically
30+
* excluded.
31+
*/
32+
@Builder.Default
33+
private List<String> excludedExtensions = List.of(".jpg", ".jpeg", ".png", ".gif", ".zip", ".jar", ".class");
34+
35+
/**
36+
* Create a default configuration with common secret patterns.
37+
*
38+
* @return A default SecretScanningConfig instance
39+
*/
40+
public static SecretScanningConfig defaultConfig() {
41+
List<String> defaultPatterns = new ArrayList<>();
42+
43+
// AWS Keys
44+
defaultPatterns.add("AKIA[0-9A-Z]{16}"); // AWS Access Key ID
45+
defaultPatterns.add("aws(.{0,20})?['\"][0-9a-zA-Z/+]{40}['\"]"); // AWS Secret Key
46+
47+
// Generic API Keys
48+
defaultPatterns.add("api[_-]?key['\"]?\\s*[:=]\\s*['\"][0-9a-zA-Z]{32,45}['\"]");
49+
50+
// Generic Passwords
51+
defaultPatterns.add("password['\"]?\\s*[:=]\\s*['\"][^'\"\\s]{8,}['\"]");
52+
53+
// Private Keys
54+
defaultPatterns.add("-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----");
55+
56+
// Generic Tokens
57+
defaultPatterns.add("token['\"]?\\s*[:=]\\s*['\"][0-9a-zA-Z]{20,}['\"]");
58+
59+
// Database Connection Strings
60+
defaultPatterns.add("(jdbc|mongodb|mysql|postgresql)://[^\\s]+:[^\\s]+@");
61+
62+
return SecretScanningConfig.builder().patterns(defaultPatterns).build();
63+
}
64+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public class GitRequestDetails {
1818
private Repository repository;
1919
private String branch; // null for fetch requests
2020
private Commit commit;
21+
private List<Commit> commits = new ArrayList<>(); // All commits in a push
22+
private String userEmail; // Email of the user performing the operation
2123
private GitProxyProvider provider;
2224
private List<GitProxyFilter> filters = new ArrayList<>();
2325
private GitResult result = GitResult.PENDING;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.finos.gitproxy.service;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
5+
/**
6+
* Dummy implementation of {@link RepositoryService} that always authorizes repositories. This implementation should be
7+
* replaced with a real implementation in production.
8+
*/
9+
@Slf4j
10+
public class DummyRepositoryService implements RepositoryService {
11+
12+
@Override
13+
public boolean isRepositoryAuthorized(String repositoryUrl) {
14+
log.debug("DummyRepositoryService: Repository {} is authorized (always true)", repositoryUrl);
15+
// Always authorize in dummy implementation
16+
return true;
17+
}
18+
19+
@Override
20+
public boolean repositoryExists(String repositoryUrl) {
21+
log.debug("DummyRepositoryService: Repository {} exists (always true)", repositoryUrl);
22+
// Always return true in dummy implementation
23+
return true;
24+
}
25+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.finos.gitproxy.service;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
5+
/**
6+
* Dummy implementation of {@link UserAuthorizationService} that always authorizes users. This implementation should be
7+
* replaced with a real implementation in production.
8+
*/
9+
@Slf4j
10+
public class DummyUserAuthorizationService implements UserAuthorizationService {
11+
12+
@Override
13+
public boolean isUserAuthorizedToPush(String userEmail, String repositoryUrl) {
14+
log.debug("DummyUserAuthorizationService: Authorizing user {} for repository {}", userEmail, repositoryUrl);
15+
// Always authorize in dummy implementation
16+
return true;
17+
}
18+
19+
@Override
20+
public boolean userExists(String userEmail) {
21+
log.debug("DummyUserAuthorizationService: User {} exists (always true)", userEmail);
22+
// Always return true in dummy implementation
23+
return true;
24+
}
25+
26+
@Override
27+
public String getUsernameByEmail(String userEmail) {
28+
log.debug("DummyUserAuthorizationService: Getting username for {}", userEmail);
29+
// Extract username from email as a placeholder
30+
if (userEmail != null && userEmail.contains("@")) {
31+
return userEmail.substring(0, userEmail.indexOf("@"));
32+
}
33+
return userEmail;
34+
}
35+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.finos.gitproxy.service;
2+
3+
/**
4+
* Service interface for repository operations. Implementations should provide repository metadata and authorization
5+
* information.
6+
*/
7+
public interface RepositoryService {
8+
9+
/**
10+
* Check if a repository is in the authorized list.
11+
*
12+
* @param repositoryUrl The URL of the repository
13+
* @return true if the repository is authorized, false otherwise
14+
*/
15+
boolean isRepositoryAuthorized(String repositoryUrl);
16+
17+
/**
18+
* Check if a repository exists.
19+
*
20+
* @param repositoryUrl The URL of the repository
21+
* @return true if the repository exists, false otherwise
22+
*/
23+
boolean repositoryExists(String repositoryUrl);
24+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.finos.gitproxy.service;
2+
3+
/**
4+
* Service interface for user authorization. Implementations should determine whether a user is authorized to perform
5+
* operations on repositories.
6+
*/
7+
public interface UserAuthorizationService {
8+
9+
/**
10+
* Check if a user is authorized to push to a specific repository.
11+
*
12+
* @param userEmail The email address of the user
13+
* @param repositoryUrl The URL of the repository
14+
* @return true if the user is authorized, false otherwise
15+
*/
16+
boolean isUserAuthorizedToPush(String userEmail, String repositoryUrl);
17+
18+
/**
19+
* Check if a user exists in the system.
20+
*
21+
* @param userEmail The email address of the user
22+
* @return true if the user exists, false otherwise
23+
*/
24+
boolean userExists(String userEmail);
25+
26+
/**
27+
* Get the username associated with an email address.
28+
*
29+
* @param userEmail The email address of the user
30+
* @return The username, or null if not found
31+
*/
32+
String getUsernameByEmail(String userEmail);
33+
}

0 commit comments

Comments
 (0)