Skip to content
This repository was archived by the owner on Sep 14, 2026. It is now read-only.

Commit d53c599

Browse files
committed
Apply Codex audit fixes
1 parent 33c7d28 commit d53c599

10 files changed

Lines changed: 50 additions & 9 deletions

File tree

.github/workflows/actionlint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ jobs:
1818
steps:
1919
- name: Checkout
2020
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
21+
with:
22+
persist-credentials: false
2123
- name: Run actionlint
2224
uses: raven-actions/actionlint@205b530c5d9fa8f44ae9ed59f341a0db994aa6f8 # v2

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
runs-on: ${{ matrix.os }}
2020
steps:
2121
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
22+
with:
23+
persist-credentials: false
2224
- name: Setup Java
2325
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
2426
with:

.github/workflows/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
security-events: write
2020
steps:
2121
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
22+
with:
23+
persist-credentials: false
2224
- name: Setup Java
2325
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
2426
with:

.github/workflows/dependency-review.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ jobs:
1414
steps:
1515
- name: Checkout
1616
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
17+
with:
18+
persist-credentials: false
1719
- name: Review dependency changes
1820
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
steps:
1717
- name: Checkout
1818
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
19+
with:
20+
persist-credentials: false
1921
- name: Set up Java
2022
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
2123
with:

gradle/wrapper/gradle-wrapper.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-all.zip
4-
networkTimeout=10000
5-
retries=0
4+
distributionSha256Sum=c72fb9991f6025cbe337d52ba77e531b3faf62bdd3e348fe1ccee9f51c71adb0
5+
networkTimeout=60000
6+
retries=3
67
retryBackOffMs=500
78
validateDistributionUrl=true
89
zipStoreBase=GRADLE_USER_HOME

src/main/java/dev/krotname/networkchat/network/AccountStore.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
/** File-backed account registry with salted SHA-256 token hashes. */
1717
public final class AccountStore {
1818
private static final String HASH_ALGORITHM = "SHA-256";
19+
private static final int MIN_USER_NAME_LENGTH = 3;
20+
private static final int MAX_USER_NAME_LENGTH = 64;
21+
private static final String USER_NAME_PATTERN = "[\\p{L}\\p{N}_-]+";
1922

2023
private final Map<String, AccountRecord> accounts;
2124
private final boolean enabled;
@@ -39,7 +42,9 @@ public static AccountStore load(Path accountFile) throws IOException {
3942
continue;
4043
}
4144
AccountRecord account = parseLine(line, lineNumber);
42-
loadedAccounts.put(account.userName(), account);
45+
if (loadedAccounts.putIfAbsent(account.userName(), account) != null) {
46+
throw new IllegalArgumentException("Duplicate account user name on line " + lineNumber);
47+
}
4348
}
4449
return new AccountStore(loadedAccounts, true);
4550
}
@@ -78,6 +83,13 @@ public int size() {
7883
return accounts.size();
7984
}
8085

86+
public static boolean isValidUserName(String userName) {
87+
return userName != null
88+
&& userName.length() >= MIN_USER_NAME_LENGTH
89+
&& userName.length() <= MAX_USER_NAME_LENGTH
90+
&& userName.matches(USER_NAME_PATTERN);
91+
}
92+
8193
private static AccountRecord parseLine(String line, int lineNumber) {
8294
String[] columns = line.split(",", -1);
8395
if (columns.length != 4) {
@@ -87,7 +99,7 @@ private static AccountRecord parseLine(String line, int lineNumber) {
8799
UserRole role = parseRole(columns[1].trim(), lineNumber);
88100
String salt = columns[2].trim();
89101
String tokenHash = columns[3].trim().toLowerCase(Locale.ROOT);
90-
if (userName.isBlank() || salt.isBlank() || tokenHash.isBlank()) {
102+
if (!isValidUserName(userName) || salt.isBlank() || tokenHash.isBlank()) {
91103
throw new IllegalArgumentException("Invalid account file line " + lineNumber);
92104
}
93105
return new AccountRecord(userName, role, salt, tokenHash);

src/main/java/dev/krotname/networkchat/network/AccountTool.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ public static void main(String[] args) {
1515
System.err.println("Usage: AccountTool <username> <USER|ADMIN> <token>");
1616
System.exit(2);
1717
}
18+
if (!AccountStore.isValidUserName(args[0])) {
19+
System.err.println("Invalid username. Use 3-64 letters, digits, '_' or '-'.");
20+
System.exit(2);
21+
}
1822
UserRole role = UserRole.valueOf(args[1].toUpperCase(Locale.ROOT));
1923
String salt = randomSalt();
2024
System.out.printf("%s,%s,%s,%s%n", args[0], role, salt, AccountStore.hashToken(salt, args[2]));

src/main/java/dev/krotname/networkchat/network/ChatServer.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
/** Chat server that authenticates users, keeps sessions, and broadcasts messages. */
2929
public final class ChatServer implements AutoCloseable {
3030
private static final Logger LOG = Logger.getLogger(ChatServer.class.getName());
31-
private static final int MIN_USER_NAME_LENGTH = 3;
32-
private static final int MAX_USER_NAME_LENGTH = 64;
3331
private static final int MIN_ROOM_NAME_LENGTH = 1;
3432
private static final int MAX_ROOM_NAME_LENGTH = 64;
3533

@@ -249,9 +247,7 @@ private HandshakeResult serverHandshake(ChatConnection connection) throws IOExce
249247
}
250248

251249
private boolean isNameValid(String userName) {
252-
return userName.length() >= MIN_USER_NAME_LENGTH
253-
&& userName.length() <= MAX_USER_NAME_LENGTH
254-
&& userName.matches("[\\p{L}\\p{N}_-]+");
250+
return AccountStore.isValidUserName(userName);
255251
}
256252

257253
/** Sends all registered users to the new client so UI state is correct right after connect. */

src/test/java/dev/krotname/networkchat/AccountStoreTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,22 @@ void rejectsRowsWithMissingColumnsOrValues() throws Exception {
5454
assertThrows(IllegalArgumentException.class, () -> AccountStore.load(missingColumns));
5555
assertThrows(IllegalArgumentException.class, () -> AccountStore.load(missingValues));
5656
}
57+
58+
@Test
59+
void rejectsDuplicateOrInvalidUserNames() throws Exception {
60+
Path duplicateUsers = tempDir.resolve("duplicate-users.csv");
61+
Path invalidUser = tempDir.resolve("invalid-user.csv");
62+
Files.writeString(
63+
duplicateUsers,
64+
"alice,USER,salt," + AccountStore.hashToken("salt", "secret") + "\n"
65+
+ "alice,ADMIN,other," + AccountStore.hashToken("other", "secret") + "\n",
66+
StandardCharsets.UTF_8);
67+
Files.writeString(
68+
invalidUser,
69+
"al,USER,salt," + AccountStore.hashToken("salt", "secret") + "\n",
70+
StandardCharsets.UTF_8);
71+
72+
assertThrows(IllegalArgumentException.class, () -> AccountStore.load(duplicateUsers));
73+
assertThrows(IllegalArgumentException.class, () -> AccountStore.load(invalidUser));
74+
}
5775
}

0 commit comments

Comments
 (0)