Skip to content

Commit 9473e08

Browse files
committed
add secret scanning endpoint
1 parent 409e230 commit 9473e08

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

src/main/java/org/mskcc/cbio/oncokb/config/SecurityConfiguration.java

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public void configure(HttpSecurity http) throws Exception {
9292
.antMatchers("/api/register").permitAll()
9393
.antMatchers("/api/activate").permitAll()
9494
.antMatchers("/api/slack").permitAll()
95+
.antMatchers("/api/secret-scanning").permitAll()
9596
// Permits the api swagger definitions through proxy
9697
.antMatchers("/api/v1/v2/api-docs").permitAll()
9798
.antMatchers("/api/private/utils/data/**").hasAnyAuthority(AuthoritiesConstants.DATA_DOWNLOAD)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package org.mskcc.cbio.oncokb.web.rest;
2+
3+
import java.lang.reflect.Type;
4+
import java.security.InvalidKeyException;
5+
import java.security.KeyFactory;
6+
import java.security.NoSuchAlgorithmException;
7+
import java.security.PublicKey;
8+
import java.security.Signature;
9+
import java.security.SignatureException;
10+
import java.security.spec.InvalidKeySpecException;
11+
import java.security.spec.X509EncodedKeySpec;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
16+
import javax.mail.MessagingException;
17+
18+
import org.apache.commons.codec.binary.Base64;
19+
import org.mskcc.cbio.oncokb.service.MailService;
20+
import org.mskcc.cbio.oncokb.service.S3Service;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.web.bind.annotation.PostMapping;
25+
import org.springframework.web.bind.annotation.RequestBody;
26+
import org.springframework.web.bind.annotation.RequestHeader;
27+
import org.springframework.web.bind.annotation.RequestMapping;
28+
import org.springframework.web.bind.annotation.RestController;
29+
import org.springframework.web.client.RestTemplate;
30+
31+
import com.google.common.reflect.TypeToken;
32+
import com.google.gson.Gson;
33+
34+
@RestController
35+
@RequestMapping("/api")
36+
public class SecretScanningController {
37+
38+
private final static String GITHUB_KEYS_URI = "https://api.github.com/meta/public_keys/secret_scanning";
39+
40+
@Autowired
41+
private MailService mailService;
42+
43+
private final Logger log = LoggerFactory.getLogger(S3Service.class);
44+
45+
@PostMapping(path = "secret-scanning")
46+
public void receiveExposedTokens(
47+
@RequestHeader("Github-Public-Key-Signature") String signature,
48+
@RequestHeader("Github-Public-Key-Identifier") String keyID,
49+
@RequestBody String exposedTokensString)
50+
{
51+
Gson gson = new Gson();
52+
Type listType = new TypeToken<ArrayList<ExposedToken>>(){}.getType();
53+
try {
54+
boolean isVerified = verifyRequestSignature(exposedTokensString, signature, keyID);
55+
if (!isVerified) {
56+
throw new Exception("Invalid secret scanning payload");
57+
}
58+
} catch (Exception e) {
59+
log.error(e.getMessage(), e);
60+
}
61+
62+
List<ExposedToken> exposedTokensObject = gson.fromJson(exposedTokensString, listType);
63+
try {
64+
mailService.sendEmailToDevTeam(
65+
"Exposed Tokens from GitHub Secret Scanning",
66+
getExposedTokensEmailBody(exposedTokensObject),
67+
null,
68+
false,
69+
false
70+
);
71+
} catch (MessagingException e) {
72+
log.error("Error sending GitHub secret scanning email", e);
73+
}
74+
}
75+
76+
public boolean verifyRequestSignature(String payload, String signature, String keyID) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
77+
if (payload.length() == 0 || signature.length() == 0 || keyID.length() == 0) {
78+
return false;
79+
}
80+
81+
RestTemplate restTemplate = new RestTemplate();
82+
GitHubValidationPayload gitHubValidationPayload = restTemplate.getForObject(GITHUB_KEYS_URI, GitHubValidationPayload.class);
83+
if (gitHubValidationPayload.getPublicKeys() == null) {
84+
return false;
85+
}
86+
87+
GitHubPublicKey publicKey = null;
88+
for (GitHubPublicKey key : gitHubValidationPayload.getPublicKeys()) {
89+
if (key.key_identifier == keyID) {
90+
publicKey = key;
91+
break;
92+
}
93+
}
94+
if (publicKey == null) {
95+
return false;
96+
}
97+
98+
String publicKeyContent = publicKey.getKey()
99+
.replace("\n", "")
100+
.replace("-----BEGIN PUBLIC KEY-----", "")
101+
.replace("-----END PUBLIC KEY-----", "");
102+
byte[] publicKeyBytes = Base64.decodeBase64(publicKeyContent);
103+
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
104+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
105+
PublicKey pubKey = keyFactory.generatePublic(publicKeySpec);
106+
Signature verifier = Signature.getInstance("SHA256withRSA");
107+
verifier.initVerify(pubKey);
108+
verifier.update(payload.getBytes());
109+
if (!verifier.verify(Base64.decodeBase64(signature))) {
110+
return false;
111+
}
112+
113+
return true;
114+
}
115+
116+
public static String getExposedTokensEmailBody(List<ExposedToken> exposedTokens) {
117+
List<String> exposedTokenStrings = exposedTokens.stream().map((token) -> token.token).collect(Collectors.toList());
118+
return "The following exposed tokens were reported by GitHub's secret scanning program:\n\n"
119+
+ String.join("\n", exposedTokenStrings);
120+
}
121+
122+
class ExposedToken {
123+
private String token;
124+
private String type;
125+
private String url;
126+
private String source;
127+
128+
public String getToken() {
129+
return token;
130+
}
131+
132+
public void setToken(String token) {
133+
this.token = token;
134+
}
135+
136+
public String getType() {
137+
return type;
138+
}
139+
140+
public void setType(String type) {
141+
this.type = type;
142+
}
143+
144+
public String getUrl() {
145+
return url;
146+
}
147+
148+
public void setUrl(String url) {
149+
this.url = url;
150+
}
151+
152+
public String getSource() {
153+
return source;
154+
}
155+
156+
public void setSource(String source) {
157+
this.source = source;
158+
}
159+
}
160+
161+
class GitHubValidationPayload {
162+
private List<GitHubPublicKey> publicKeys;
163+
private boolean is_current;
164+
165+
public List<GitHubPublicKey> getPublicKeys() {
166+
return publicKeys;
167+
}
168+
169+
public void setPublicKeys(List<GitHubPublicKey> publicKeys) {
170+
this.publicKeys = publicKeys;
171+
}
172+
173+
public boolean isIs_current() {
174+
return is_current;
175+
}
176+
177+
public void setIs_current(boolean is_current) {
178+
this.is_current = is_current;
179+
}
180+
}
181+
182+
class GitHubPublicKey {
183+
private String key_identifier;
184+
private String key;
185+
186+
public String getKey_identifier() {
187+
return key_identifier;
188+
}
189+
public void setKey_identifier(String key_identifier) {
190+
this.key_identifier = key_identifier;
191+
}
192+
193+
public String getKey() {
194+
return key;
195+
}
196+
197+
public void setKey(String key) {
198+
this.key = key;
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)