Skip to content

Commit ecc6055

Browse files
committed
tests/github_secret_scanning: Use non-hardcoded payload signatures
This will allow us to test with other payloads in the future.
1 parent 7a9f2e0 commit ecc6055

File tree

1 file changed

+47
-21
lines changed

1 file changed

+47
-21
lines changed

src/tests/github_secret_scanning.rs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,59 @@ use crate::tests::util::MockRequestExt;
22
use crate::tests::{RequestHelper, TestApp};
33
use crate::util::token::HashedToken;
44
use crate::{models::ApiToken, schema::api_tokens};
5+
use base64::{Engine as _, engine::general_purpose};
56
use crates_io_github::{GitHubPublicKey, MockGitHubClient};
67
use diesel::prelude::*;
78
use diesel_async::RunQueryDsl;
89
use googletest::prelude::*;
910
use insta::{assert_json_snapshot, assert_snapshot};
11+
use p256::ecdsa::{Signature, SigningKey, signature::Signer};
12+
use p256::pkcs8::DecodePrivateKey;
13+
use std::sync::LazyLock;
1014

1115
static URL: &str = "/api/github/secret-scanning/verify";
1216

13-
// Test request and signature from https://docs.github.com/en/developers/overview/secret-scanning-partner-program#create-a-secret-alert-service
17+
// Test request payload for GitHub secret scanning
1418
static GITHUB_ALERT: &[u8] =
1519
br#"[{"token":"some_token","type":"some_type","url":"some_url","source":"some_source"}]"#;
1620

17-
static GITHUB_PUBLIC_KEY_IDENTIFIER: &str =
18-
"f9525bf080f75b3506ca1ead061add62b8633a346606dc5fe544e29231c6ee0d";
19-
20-
/// Test key from https://docs.github.com/en/developers/overview/secret-scanning-partner-program#create-a-secret-alert-service
21-
static GITHUB_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsz9ugWDj5jK5ELBK42ynytbo38gP\nHzZFI03Exwz8Lh/tCfL3YxwMdLjB+bMznsanlhK0RwcGP3IDb34kQDIo3Q==\n-----END PUBLIC KEY-----";
22-
23-
static GITHUB_PUBLIC_KEY_SIGNATURE: &str = "MEUCIFLZzeK++IhS+y276SRk2Pe5LfDrfvTXu6iwKKcFGCrvAiEAhHN2kDOhy2I6eGkOFmxNkOJ+L2y8oQ9A2T9GGJo6WJY=";
21+
/// Private key for signing payloads (ECDSA P-256)
22+
///
23+
/// Generated specifically for testing - do not use in production.
24+
///
25+
/// This corresponds to the public key below and is used to generate valid signatures
26+
static PRIVATE_KEY: &str = r#"-----BEGIN PRIVATE KEY-----
27+
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgV64BdEFXg9aT/m4p
28+
wOQ/o9WUHxZ6qfBaP3D7Km1TOWuhRANCAARYKkbkTbIr//8klg1CMYGQIwtlfNd4
29+
JQYV5+q0s3+JnBSLb1/sx/lEDzmMVZQIZQrACUHFW4UVdmox2NvmNWyy
30+
-----END PRIVATE KEY-----"#;
31+
32+
/// Public key (corresponds to the private key above)
33+
static PUBLIC_KEY: &str = r#"-----BEGIN PUBLIC KEY-----
34+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWCpG5E2yK///JJYNQjGBkCMLZXzX
35+
eCUGFefqtLN/iZwUi29f7Mf5RA85jFWUCGUKwAlBxVuFFXZqMdjb5jVssg==
36+
-----END PUBLIC KEY-----"#;
37+
38+
/// Public key identifier (SHA256 hash of the DER-encoded public key)
39+
static KEY_IDENTIFIER: &str = "2aafbbe2d329af78d875cd2dd0291048799176466844315b6a846d6e12aa26ca";
40+
41+
/// Signing key derived from the private key
42+
static SIGNING_KEY: LazyLock<SigningKey> =
43+
LazyLock::new(|| SigningKey::from_pkcs8_pem(PRIVATE_KEY).unwrap());
44+
45+
/// Generate a signature for the payload using our private key
46+
fn sign_payload(payload: &[u8]) -> String {
47+
let signature: Signature = SIGNING_KEY.sign(payload);
48+
general_purpose::STANDARD.encode(signature.to_der())
49+
}
2450

2551
fn github_mock() -> MockGitHubClient {
2652
let mut mock = MockGitHubClient::new();
2753

2854
mock.expect_public_keys().returning(|_, _| {
2955
let key = GitHubPublicKey {
30-
key_identifier: GITHUB_PUBLIC_KEY_IDENTIFIER.to_string(),
31-
key: GITHUB_PUBLIC_KEY.to_string(),
56+
key_identifier: KEY_IDENTIFIER.to_string(),
57+
key: PUBLIC_KEY.to_string(),
3258
is_current: true,
3359
};
3460

@@ -70,8 +96,8 @@ async fn github_secret_alert_revokes_token() {
7096

7197
let mut request = anon.post_request(URL);
7298
*request.body_mut() = GITHUB_ALERT.into();
73-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
74-
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", GITHUB_PUBLIC_KEY_SIGNATURE);
99+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
100+
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", &sign_payload(GITHUB_ALERT));
75101
let response = anon.run::<()>(request).await;
76102
assert_snapshot!(response.status(), @"200 OK");
77103
assert_json_snapshot!(response.json());
@@ -134,8 +160,8 @@ async fn github_secret_alert_for_revoked_token() {
134160

135161
let mut request = anon.post_request(URL);
136162
*request.body_mut() = GITHUB_ALERT.into();
137-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
138-
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", GITHUB_PUBLIC_KEY_SIGNATURE);
163+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
164+
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", &sign_payload(GITHUB_ALERT));
139165
let response = anon.run::<()>(request).await;
140166
assert_snapshot!(response.status(), @"200 OK");
141167
assert_json_snapshot!(response.json());
@@ -187,8 +213,8 @@ async fn github_secret_alert_for_unknown_token() {
187213

188214
let mut request = anon.post_request(URL);
189215
*request.body_mut() = GITHUB_ALERT.into();
190-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
191-
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", GITHUB_PUBLIC_KEY_SIGNATURE);
216+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
217+
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", &sign_payload(GITHUB_ALERT));
192218
let response = anon.run::<()>(request).await;
193219
assert_snapshot!(response.status(), @"200 OK");
194220
assert_json_snapshot!(response.json());
@@ -225,30 +251,30 @@ async fn github_secret_alert_invalid_signature_fails() {
225251

226252
// Headers but no request body
227253
let mut request = anon.post_request(URL);
228-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
229-
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", GITHUB_PUBLIC_KEY_SIGNATURE);
254+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
255+
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", &sign_payload(GITHUB_ALERT));
230256
let response = anon.run::<()>(request).await;
231257
assert_snapshot!(response.status(), @"400 Bad Request");
232258

233259
// Request body but only key identifier header
234260
let mut request = anon.post_request(URL);
235261
*request.body_mut() = GITHUB_ALERT.into();
236-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
262+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
237263
let response = anon.run::<()>(request).await;
238264
assert_snapshot!(response.status(), @"400 Bad Request");
239265

240266
// Invalid signature
241267
let mut request = anon.post_request(URL);
242268
*request.body_mut() = GITHUB_ALERT.into();
243-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
269+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
244270
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", "bad signature");
245271
let response = anon.run::<()>(request).await;
246272
assert_snapshot!(response.status(), @"400 Bad Request");
247273

248274
// Invalid signature that is valid base64
249275
let mut request = anon.post_request(URL);
250276
*request.body_mut() = GITHUB_ALERT.into();
251-
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", GITHUB_PUBLIC_KEY_IDENTIFIER);
277+
request.header("GITHUB-PUBLIC-KEY-IDENTIFIER", KEY_IDENTIFIER);
252278
request.header("GITHUB-PUBLIC-KEY-SIGNATURE", "YmFkIHNpZ25hdHVyZQ==");
253279
let response = anon.run::<()>(request).await;
254280
assert_snapshot!(response.status(), @"400 Bad Request");

0 commit comments

Comments
 (0)