////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2013 Denim Group, Ltd.
//
// The contents of this file are subject to the Mozilla Public License
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is ThreadFix.
//
// The Initial Developer of the Original Code is Denim Group, Ltd.
// Portions created by Denim Group, Ltd. are Copyright (C)
// Denim Group, Ltd. All Rights Reserved.
//
// Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service.remoteprovider;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.denimgroup.threadfix.data.dao.ChannelSeverityDao;
import com.denimgroup.threadfix.data.dao.ChannelTypeDao;
import com.denimgroup.threadfix.data.dao.ChannelVulnerabilityDao;
import com.denimgroup.threadfix.data.entities.ChannelType;
import com.denimgroup.threadfix.data.entities.Finding;
import com.denimgroup.threadfix.data.entities.RemoteProviderApplication;
import com.denimgroup.threadfix.data.entities.Scan;
import com.denimgroup.threadfix.service.channel.HandlerWithBuilder;
public class WhiteHatRemoteProvider extends RemoteProvider {
private static final String SITES_URL = "https://sentinel.whitehatsec.com/api/site/";
private static final String VULNS_URL = "https://sentinel.whitehatsec.com/api/vuln/";
private static final String EXTRA_PARAMS = "&display_attack_vectors=1&query_status=open&query_site=";
private String apiKey = null;
@Autowired
public WhiteHatRemoteProvider(ChannelTypeDao channelTypeDao,
ChannelVulnerabilityDao channelVulnerabilityDao,
ChannelSeverityDao channelSeverityDao) {
this.channelVulnerabilityDao = channelVulnerabilityDao;
this.channelTypeDao = channelTypeDao;
this.channelSeverityDao = channelSeverityDao;
setChannelType(ChannelType.SENTINEL);
}
@Override
public List<Scan> getScans(RemoteProviderApplication remoteProviderApplication) {
log.info("Retrieving a WhiteHat scan.");
apiKey = remoteProviderApplication.getRemoteProviderType().getApiKey();
InputStream labelSiteIdStream = httpGet(SITES_URL + "?key=" + apiKey);
if (labelSiteIdStream == null) {
log.warn("Received a bad response from WhiteHat servers, returning null.");
return null;
}
String appName = remoteProviderApplication.getNativeId();
WhiteHatSitesParser parser = new WhiteHatSitesParser();
parse(labelSiteIdStream, parser);
String siteId = parser.map.get(appName);
if (siteId == null) {
log.warn("No build ID was parsed.");
return null; // we failed.
} else {
log.info("Retrieved build ID " + siteId + " for application " + appName);
}
String url = VULNS_URL + "?key=" + apiKey + EXTRA_PARAMS + siteId;
log.info("Requesting site ID " + siteId);
inputStream = httpGet(url);
if (inputStream == null) {
log.warn("Received a bad response from WhiteHat servers, returning null.");
return null;
}
WhiteHatVulnerabilitiesParser scanParser = new WhiteHatVulnerabilitiesParser();
Scan resultScan = parseSAXInput(scanParser);
if (resultScan == null) {
log.warn("No scan was parsed, returning null.");
return null;
}
resultScan.setApplicationChannel(remoteProviderApplication.getApplicationChannel());
log.info("WhiteHat scan successfully parsed.");
List<Scan> scans = new ArrayList<Scan>();
scans.add(resultScan);
return scans;
}
@Override
public List<RemoteProviderApplication> fetchApplications() {
if (remoteProviderType == null || remoteProviderType.getApiKey() == null) {
log.warn("Insufficient credentials.");
return null;
}
apiKey = remoteProviderType.getApiKey();
WhiteHatSitesParser parser = new WhiteHatSitesParser();
InputStream stream = httpGet(SITES_URL + "?key=" + apiKey);
parse(stream, parser);
return parser.getApplications();
}
public InputStream httpGet(String urlStr) {
GetMethod get = new GetMethod(urlStr);
InputStream responseStream = null;
HttpClient client = new HttpClient();
try {
int status = client.executeMethod(get);
if (status != 200) {
log.warn("Request status was not 200. It was " + status);
}
responseStream = get.getResponseBodyAsStream();
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseStream;
}
public class WhiteHatSitesParser extends HandlerWithBuilder {
public Map<String, String> map = new HashMap<String,String>();
private String currentId = null;
private boolean grabLabel;
public List<RemoteProviderApplication> getApplications() {
List<RemoteProviderApplication> apps = new ArrayList<RemoteProviderApplication>();
for (String label : map.keySet()) {
RemoteProviderApplication remoteProviderApplication = new RemoteProviderApplication();
remoteProviderApplication.setNativeId(label);
remoteProviderApplication.setRemoteProviderType(remoteProviderType);
apps.add(remoteProviderApplication);
}
return apps;
}
public void startElement(String uri, String name, String qName, Attributes atts) throws SAXException {
if ("site".equals(qName)) {
currentId = atts.getValue("id");
} else if ("label".equals(qName)) {
grabLabel = true;
}
}
public void endElement(String uri, String name, String qName) {
if (grabLabel) {
String text = getBuilderText();
if (text != null) {
map.put(text, currentId);
}
currentId = null;
grabLabel = false;
}
}
public void characters (char ch[], int start, int length) {
if (grabLabel) {
addTextToBuilder(ch, start, length);
}
}
}
public class WhiteHatVulnerabilitiesParser extends DefaultHandler {
public Finding finding = new Finding();
private Map<FindingKey, String> map = new EnumMap<>(FindingKey.class);
private boolean creatingVuln = false;
private void addFinding() {
Finding finding = constructFinding(map);
if (finding == null) {
log.warn("Finding was null.");
} else {
finding.setNativeId(map.get(FindingKey.NATIVE_ID));
}
saxFindingList.add(finding);
}
public void startElement (String uri, String name, String qName, Attributes atts) throws SAXException {
if ("vulnerability".equals(qName) && "open".equals(atts.getValue("status"))) {
creatingVuln = true;
map.clear();
map.put(FindingKey.NATIVE_ID, atts.getValue("id"));
map.put(FindingKey.VULN_CODE, atts.getValue("class"));
map.put(FindingKey.SEVERITY_CODE, atts.getValue("severity"));
} else if (creatingVuln) {
if (qName.equals("request")) {
map.put(FindingKey.PATH, atts.getValue("url"));
} else if (qName.equals("param")) {
map.put(FindingKey.PARAMETER, atts.getValue("name"));
}
if (map.get(FindingKey.NATIVE_ID) != null &&
map.get(FindingKey.VULN_CODE) != null &&
map.get(FindingKey.SEVERITY_CODE) != null &&
map.get(FindingKey.PATH) != null &&
map.get(FindingKey.PARAMETER) != null) {
creatingVuln = false;
addFinding();
}
}
}
@Override
public void endElement (String uri, String localName, String qName) throws SAXException {
if (qName.equals("attack_vectors")) {
addFinding();
creatingVuln = false;
}
}
}
}