Skip to content

Commit da2e752

Browse files
committed
Merge branch 'main' into fix/scan-endpoint-auth
Signed-off-by: Tetiana Naumenko <t.naumenko@samsung.com>
2 parents 44d7287 + 287329b commit da2e752

10 files changed

Lines changed: 200 additions & 41 deletions

File tree

‎.env.example‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ MYSQL_ROOT_PASSWORD=
1010
MYSQL_USER=lpvs
1111
MYSQL_PASSWORD=
1212

13+
# Secret used to verify GitHub webhook signatures (X-Hub-Signature-256). Must match
14+
# the "Secret" configured in the GitHub webhook settings. Generate a strong random
15+
# value, e.g. `openssl rand -hex 32`.
16+
LPVS_GITHUB_SECRET=
17+
1318
# Optional API key for the single scan endpoint `POST /scan/{org}/{repo}/{pr}`,
1419
# sent by callers in the `X-LPVS-Api-Key` header. Leave empty to disable the
1520
# endpoint. Generate a strong random value, e.g. `openssl rand -hex 32`.

‎doc/docs/quick-start-guide.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This section explains how to download and run pre-built LPVS Docker image with n
2323
### Set up LPVS Docker environment variables
2424

2525
* Copy `.env.example` to `.env` in the same directory as `docker-compose-quick.yml`, and
26-
fill in strong, unique values for `MYSQL_ROOT_PASSWORD` and `MYSQL_PASSWORD` (refer to the
26+
fill in strong, unique values for `MYSQL_ROOT_PASSWORD`, `MYSQL_PASSWORD` and `LPVS_GITHUB_SECRET` (refer to the
2727
[guide](user-guide/service/docker.md#setting-up-lpvs-docker-environment-variables)). `docker compose`
2828
refuses to start if these are left empty.
2929

‎doc/docs/user-guide/config/options.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ for more details.
3939

4040
- `github.secret`: This setting specifies the secret string used for configuring webhooks. Please refer to the
4141
[Webhook configuration guide](../service/webhook.md#configure-the-webhook-in-your-github-repository-settings)
42-
for more details. Default: `LPVS`.
42+
for more details. Required, no default: LPVS refuses to start if it is empty.
4343

4444
- `lpvs.*`: These settings include various configurations specific to the LPVS application like core pool size,
4545
number of scan attempts, and version.
@@ -115,7 +115,7 @@ Alternatively, you can provide the necessary values for several properties using
115115
- `LPVS_GITHUB_LOGIN`: Equivalent to the property `github.login`.
116116
- `LPVS_GITHUB_TOKEN`: Equivalent to the property `github.token`.
117117
- `LPVS_GITHUB_API_URL`: Equivalent to the property `github.api.url`.
118-
- `LPVS_GITHUB_SECRET`: Equivalent to the property `github.secret`.
118+
- `LPVS_GITHUB_SECRET`: Equivalent to the property `github.secret`. Takes precedence over the property if both are set.
119119
- `LPVS_LICENSE_CONFLICT`: Equivalent to the property `license_conflict`.
120120
- `LPVS_API_KEY`: Equivalent to the property `lpvs.api.key`. Takes precedence over the property if both are set.
121121
- `LPVS_DATASOURCE_USERNAME`: Equivalent to the property `spring.datasource.username`. Required — the

‎doc/docs/user-guide/service/docker.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ For the Docker deployment scenario, you need to provide database credentials and
1010
GitHub settings before starting the containers.
1111

1212
* Copy `.env.example` to `.env` in the same directory as `docker-compose.yml`, and
13-
fill in strong, unique values for `MYSQL_ROOT_PASSWORD` and `MYSQL_PASSWORD`:
13+
fill in strong, unique values for `MYSQL_ROOT_PASSWORD`, `MYSQL_PASSWORD` and `LPVS_GITHUB_SECRET`:
1414

1515
```bash
1616
cp .env.example .env

‎doc/docs/user-guide/service/webhook.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ Follow the next steps:
5353
It will look like `https://50be-62-205-136-206.ngrok-free.app/`.
5454

5555
- Specify the content type as `application/json`.
56-
- Fill in the `Secret` field with the passphrase: `LPVS`.
57-
- Save the same passphrase in `github.secret` of the LPVS backend `application.properties` or `docker-compose.yml` files.
56+
- Fill in the `Secret` field with a strong random passphrase, e.g. generated with `openssl rand -hex 32`.
57+
- Save the same passphrase as `LPVS_GITHUB_SECRET` in the `.env` file (Docker deployment), or in `github.secret`
58+
of the LPVS backend `application.properties` file. LPVS refuses to start if the secret is empty.
5859

5960
![step3](../../img/webhook/step_1_3.png)
6061

‎docker-compose-quick.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ services:
2121
- github.login=
2222
- github.token=
2323
- github.api.url=https://api.github.com
24-
- github.secret=LPVS
24+
- github.secret=${LPVS_GITHUB_SECRET:?Set LPVS_GITHUB_SECRET in .env (see .env.example)}
2525

2626
depends_on:
2727
mysqldb:

‎docker-compose.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ services:
2121
- github.login=
2222
- github.token=
2323
- github.api.url=https://api.github.com
24-
- github.secret=LPVS
24+
- github.secret=${LPVS_GITHUB_SECRET:?Set LPVS_GITHUB_SECRET in .env (see .env.example)}
2525

2626
depends_on:
2727
mysqldb:

‎src/main/java/com/lpvs/controller/GitHubController.java‎

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,40 @@ public class GitHubController {
6161

6262
/**
6363
* Initializes the GitHub secret from the LPVS_GITHUB_SECRET environment variable or the application property.
64+
* The environment variable takes precedence over the property.
6465
* Exits the application if the secret is not set.
6566
*/
6667
@PostConstruct
6768
public void initializeGitHubController() {
6869
this.GITHUB_SECRET =
69-
Optional.ofNullable(this.GITHUB_SECRET)
70-
.filter(s -> !s.isEmpty())
71-
.orElse(
72-
Optional.ofNullable(System.getenv("LPVS_GITHUB_SECRET"))
73-
.orElse(""));
74-
if (this.GITHUB_SECRET.isEmpty()) {
75-
log.error("LPVS_GITHUB_SECRET (github.secret) is not set.");
76-
exitHandler.exit(-1);
70+
Optional.ofNullable(System.getenv("LPVS_GITHUB_SECRET"))
71+
.filter(StringUtils::hasText)
72+
.orElse(Optional.ofNullable(this.GITHUB_SECRET).orElse(""));
73+
if (!StringUtils.hasText(this.GITHUB_SECRET)) {
74+
if (StringUtils.hasText(pullRequestTrigger) || StringUtils.hasText(localPath)) {
75+
// Single scan (CLI) mode doesn't need webhooks: keep running, reject webhooks
76+
log.warn(
77+
"LPVS_GITHUB_SECRET (github.secret) is not set. Webhook endpoint is disabled.");
78+
this.GITHUB_SECRET = "";
79+
} else {
80+
log.error("LPVS_GITHUB_SECRET (github.secret) is not set.");
81+
exitHandler.exit(-1);
82+
}
7783
}
7884
}
7985

86+
/**
87+
* Trigger value to start a single scan of a pull request (optional).
88+
*/
89+
@Value("${github.pull.request:}")
90+
private String pullRequestTrigger;
91+
92+
/**
93+
* Trigger value to start a single scan of local files or folder (optional).
94+
*/
95+
@Value("${local.path:}")
96+
private String localPath;
97+
8098
/**
8199
* LPVSQueueService for handling user-related business logic.
82100
*/
@@ -107,6 +125,7 @@ public void initializeGitHubController() {
107125
private static final String SUCCESS = "Success";
108126
private static final String ERROR = "Error";
109127
private static final String ALGORITHM = "HmacSHA256";
128+
private static final String SIGNATURE_PREFIX = "sha256=";
110129

111130
/**
112131
* Constructor for GitHubController.
@@ -175,7 +194,7 @@ public ResponseEntity<LPVSResponseWrapper> gitHubWebhooks(
175194
.headers(LPVSPayloadUtil.generateSecurityHeaders())
176195
.body(new LPVSResponseWrapper(ERROR));
177196
}
178-
if (!GITHUB_SECRET.trim().isEmpty() && wrongSecret(signature, payload)) {
197+
if (!StringUtils.hasText(GITHUB_SECRET) || wrongSecret(signature, payload)) {
179198
log.error("Received empty or incorrect GITHUB_SECRET");
180199
return ResponseEntity.status(HttpStatus.FORBIDDEN)
181200
.headers(LPVSPayloadUtil.generateSecurityHeaders())
@@ -310,16 +329,25 @@ public ResponseEntity<LPVSResponseWrapper> gitHubSingleScan(
310329
* @throws Exception if an error occurs during signature verification.
311330
*/
312331
public boolean wrongSecret(String signature, String payload) throws Exception {
313-
String lpvsSecret = signature.split("=", 2)[1];
332+
if (signature == null || !signature.startsWith(SIGNATURE_PREFIX)) {
333+
return true;
334+
}
335+
String lpvsSecret = signature.substring(SIGNATURE_PREFIX.length());
314336

315-
SecretKeySpec key = new SecretKeySpec(GITHUB_SECRET.getBytes("utf-8"), ALGORITHM);
337+
SecretKeySpec key =
338+
new SecretKeySpec(GITHUB_SECRET.getBytes(StandardCharsets.UTF_8), ALGORITHM);
316339
Mac mac = Mac.getInstance(ALGORITHM);
317340
mac.init(key);
318-
String githubSecret = Hex.encodeHexString(mac.doFinal(payload.getBytes("utf-8")));
341+
String githubSecret =
342+
Hex.encodeHexString(
343+
mac.doFinal(
344+
Optional.ofNullable(payload)
345+
.orElse("")
346+
.getBytes(StandardCharsets.UTF_8)));
319347

320-
log.debug("lpvs signature: " + lpvsSecret);
321-
log.debug("github signature: " + githubSecret);
322-
323-
return !lpvsSecret.equals(githubSecret);
348+
// Constant-time comparison to prevent timing attacks
349+
return !MessageDigest.isEqual(
350+
lpvsSecret.getBytes(StandardCharsets.UTF_8),
351+
githubSecret.getBytes(StandardCharsets.UTF_8));
324352
}
325353
}

‎src/main/resources/application.properties‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ github.login=
2626
github.token=
2727
# Corresponding env. variable LPVS_GITHUB_API_URL
2828
github.api.url=https://api.github.com
29-
# Corresponding env. variable LPVS_GITHUB_SECRET
30-
github.secret=LPVS
29+
# Webhook secret (must match the "Secret" set in GitHub webhook settings). Required, no default.
30+
# Use a strong random value, e.g. `openssl rand -hex 32`.
31+
# Corresponding env. variable LPVS_GITHUB_SECRET (takes precedence over this property)
32+
github.secret=
3133

3234
# API key for the single scan endpoint (X-LPVS-Api-Key header). If empty, the endpoint is disabled.
3335
# Use a strong random value, e.g. `openssl rand -hex 32`.

0 commit comments

Comments
 (0)