├── .gitignore ├── .travis.yml ├── BappDescription.html ├── BappManifest.bmf ├── README.md ├── build.gradle ├── csp-auditor-burp-plugin ├── build.gradle └── src │ └── main │ └── java │ └── burp │ ├── BurpExtender.java │ ├── BurpPolicyBuilder.java │ ├── CspTab.java │ ├── scanner │ ├── BurpCspIssue.java │ ├── CspHeaderScanner.java │ └── MockHttpRequestResponse.java │ └── tab │ ├── ConfigurationHelperTab.java │ ├── RequestResponsePanel.java │ └── StaticMessageEditor.java ├── csp-auditor-core ├── build.gradle └── src │ ├── main │ ├── java │ │ └── ca │ │ │ └── gosecure │ │ │ └── cspauditor │ │ │ ├── gui │ │ │ ├── CspHeadersPanel.java │ │ │ └── generator │ │ │ │ ├── CspGeneratorPanel.form │ │ │ │ ├── CspGeneratorPanel.java │ │ │ │ ├── CspGeneratorPanelController.java │ │ │ │ ├── CspGeneratorPanelUiProvider.java │ │ │ │ ├── RequestResponse.java │ │ │ │ └── SortedUniqueComboBoxModel.java │ │ │ ├── model │ │ │ ├── ContentSecurityPolicy.java │ │ │ ├── CspIssue.java │ │ │ ├── Directive.java │ │ │ ├── HeaderValidation.java │ │ │ ├── WeakCdnHost.java │ │ │ └── generator │ │ │ │ └── DetectInlineJavascript.java │ │ │ └── util │ │ │ ├── PolicyBuilder.java │ │ │ └── SimpleListFile.java │ └── resources │ │ └── resources │ │ ├── Media │ │ ├── scan_issue_decoration_info_certain.png │ │ ├── scan_issue_high_certain_rpt.png │ │ ├── scan_issue_low_certain_rpt.png │ │ └── scan_issue_medium_certain_rpt.png │ │ ├── data │ │ ├── csp_host_user_content.txt │ │ ├── csp_host_vulnerable_js.txt │ │ └── js_inline_event.txt │ │ └── descriptions │ │ ├── issue_deprecated_header_name.htm │ │ ├── issue_risky_host_known_vulnerable_js.htm │ │ ├── issue_risky_host_user_content.htm │ │ ├── issue_script_unsafe_eval.htm │ │ ├── issue_script_unsafe_inline.htm │ │ ├── issue_script_wildcard.htm │ │ ├── issue_style.htm │ │ └── issue_wildcard_limited.htm │ └── test │ └── java │ └── ca │ └── gosecure │ └── cspauditor │ ├── BaseCspTest.java │ ├── gui │ ├── CspGeneratorPanelTest.java │ └── CspHeadersPanelTest.java │ ├── model │ ├── HeaderValidationTest.java │ └── WeakCdnHostTest.java │ └── util │ └── ContentSecurityPolicyBuilderTest.java ├── csp-auditor-zap-plugin ├── build.gradle ├── libs │ └── zap-2.5.0.jar └── src │ └── main │ ├── java │ └── org │ │ └── zaproxy │ │ └── zap │ │ └── extension │ │ └── cspauditor │ │ ├── CspAuditorExtension.java │ │ ├── CspAuditorPassiveScanner.java │ │ ├── ResponseCspView.java │ │ ├── ResponseCspViewFactory.java │ │ ├── ResponseCspViewSelector.java │ │ ├── ResponseImageMetadataViewSelectorFactory.java │ │ └── ZapPolicyBuilder.java │ └── resources │ └── ZapAddOn.xml ├── demo.gif ├── demo2.gif ├── downloads ├── csp-auditor-burp-1.jar └── cspauditor-alpha-1.zap ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #Eclipse 2 | .classpath 3 | .project 4 | test-output 5 | .settings 6 | 7 | #IntelliJ 8 | *.iml 9 | *.ipr 10 | *.iws 11 | .idea/ 12 | 13 | #Gradle 14 | .gradle 15 | classes/ 16 | 17 | 18 | #Build directories 19 | bin/ 20 | build/ 21 | target/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | - openjdk9 5 | - openjdk10 6 | - openjdk11 -------------------------------------------------------------------------------- /BappDescription.html: -------------------------------------------------------------------------------- 1 |

This extension provides a readable view of CSP headers for responses. It also 2 | includes passive scan rules to detect weak CSP configurations.

3 | -------------------------------------------------------------------------------- /BappManifest.bmf: -------------------------------------------------------------------------------- 1 | Uuid: 35237408a06043e9945a11016fcbac18 2 | ExtensionType: 1 3 | Name: CSP Auditor 4 | RepoName: csp-auditor 5 | ScreenVersion: 1.0 6 | SerialVersion: 1 7 | MinPlatformVersion: 0 8 | ProOnly: False 9 | Author: Philippe Arteau of GoSecure 10 | ShortDescription: Displays CSP headers for responses, and passively reports CSP weaknesses. 11 | EntryPoint: csp-auditor-burp-plugin/build/libs/csp-auditor-burp-1.jar 12 | BuildCommand: gradle :csp-auditor-burp-plugin:jar 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSP Auditor [![Build Status](https://travis-ci.org/GoSecure/csp-auditor.png)](https://travis-ci.org/GoSecure/csp-auditor) 2 | 3 | This plugin provides: 4 | 5 | * a readable view of CSP Headers in Response Tab 6 | * passive scan rules to detect weak CSP configuration 7 | * a CSP configuration generator based on the Burp crawler or using manual browsing 8 | 9 | This project is packaged as a ZAP and Burp plugin. 10 | 11 | ## Download 12 | 13 | Last updated : August 3th 2017 14 | 15 | - [Burp plugin](https://github.com/GoSecure/csp-auditor/blob/master/downloads/csp-auditor-burp-1.jar?raw=true) 16 | - [ZAP plugin](https://github.com/GoSecure/csp-auditor/blob/master/downloads/cspauditor-alpha-1.zap?raw=true) 17 | 18 | ## Screenshots 19 | 20 | Passive rules and custom tab: 21 | 22 | ![CSP Auditor Burp Plugin](https://raw.githubusercontent.com/GoSecure/csp-auditor/master/demo.gif) 23 | 24 | Configuration builder: 25 | 26 | ![CSP Auditor Burp Plugin](https://raw.githubusercontent.com/GoSecure/csp-auditor/master/demo2.gif) 27 | 28 | ## Building the plugin 29 | 30 | Type the following command: 31 | 32 | ``` 33 | ./gradlew build 34 | ``` 35 | 36 | or if you have already Gradle installed on your machine: 37 | 38 | ``` 39 | gradle build 40 | ``` 41 | 42 | ## Read more 43 | 44 | For more context around Content-Security-Policy and how to apply it to your website see our blog posts on the topic: 45 | 46 | * http://gosecure.net/2017/07/20/building-a-content-security-policy-configuration-with-csp-auditor 47 | * https://gosecure.net/2016/06/28/auditing-csp-headers-with-burp-and-zap/ 48 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'csp-auditor' 2 | version '2.0-SNAPSHOT' 3 | 4 | subprojects { //Common configuration for subprojects 5 | apply plugin: 'java' 6 | 7 | sourceCompatibility = '1.8' 8 | targetCompatibility = '1.8' 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | } 14 | 15 | 16 | subprojects { 17 | 18 | compileJava { 19 | options.encoding = 'UTF-8' 20 | //options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 21 | options.compilerArgs << "-Xlint:none" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | group 'csp-auditor' 2 | version '2.0-SNAPSHOT' 3 | 4 | dependencies { 5 | compile group: 'com.esotericsoftware', name: 'minlog', version: '1.3' 6 | compile group: 'net.portswigger.burp.extender', name: 'burp-extender-api', version: '1.7.22' 7 | testCompile group: 'org.testng', name: 'testng', version: '6.8.8' 8 | compile project(':csp-auditor-core') 9 | } 10 | 11 | jar { 12 | archiveName 'csp-auditor-burp-2.jar' 13 | from { 14 | configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import burp.scanner.CspHeaderScanner; 4 | import burp.tab.ConfigurationHelperTab; 5 | import com.esotericsoftware.minlog.Log; 6 | 7 | import java.io.PrintWriter; 8 | 9 | public class BurpExtender implements IBurpExtender, IMessageEditorTabFactory { 10 | 11 | private IBurpExtenderCallbacks callbacks; 12 | private IExtensionHelpers helpers; 13 | private CspHeaderScanner scanner; 14 | 15 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { 16 | 17 | this.callbacks = callbacks; 18 | this.helpers = callbacks.getHelpers(); 19 | this.callbacks.setExtensionName("CSP Auditor"); 20 | 21 | PrintWriter stdout = new PrintWriter(callbacks.getStdout(), true); 22 | stdout.println("== CSP Auditor plugin =="); 23 | stdout.println("This plugin provided a readable view of CSP headers in Response Tab. "); 24 | stdout.println("It also include Passive scan rules to detect weak CSP configuration."); 25 | stdout.println(" - Github : https://github.com/GoSecure/csp-auditor"); 26 | stdout.println(""); 27 | stdout.println("== License =="); 28 | stdout.println("CSP Auditor plugin is release under LGPL."); 29 | stdout.println(""); 30 | 31 | Log.setLogger(new Log.Logger() { 32 | @Override 33 | protected void print(String message) { 34 | callbacks.printOutput(message); 35 | } 36 | }); 37 | Log.DEBUG(); 38 | 39 | this.callbacks.registerMessageEditorTabFactory(this); 40 | 41 | scanner = new CspHeaderScanner(helpers); 42 | this.callbacks.registerScannerCheck(scanner); 43 | this.callbacks.addSuiteTab(new ConfigurationHelperTab(this.callbacks)); 44 | } 45 | 46 | 47 | @Override 48 | public IMessageEditorTab createNewInstance(IMessageEditorController iMessageEditorController, boolean b) { 49 | return new CspTab(this.callbacks, this.helpers, iMessageEditorController); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/BurpPolicyBuilder.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 4 | import ca.gosecure.cspauditor.util.PolicyBuilder; 5 | 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class BurpPolicyBuilder extends PolicyBuilder { 11 | 12 | public static Map getCspHeader(IResponseInfo response) { 13 | Map headers = new HashMap<>(); 14 | for(String header : response.getHeaders()) { 15 | String headerLower = header.toLowerCase(); 16 | 17 | for(String cspHeader : CSP_HEADERS) { 18 | if (headerLower.startsWith(cspHeader)) { 19 | String[] parts = header.split(":",2); 20 | if(parts.length>1) { 21 | headers.put(cspHeader, parts[1]); 22 | } 23 | } 24 | } 25 | } 26 | return headers; 27 | } 28 | 29 | public static List buildFromResponse(IResponseInfo responseInfo) { 30 | Map headers = getCspHeader(responseInfo); 31 | return parseCspHeaders(headers); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/CspTab.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import ca.gosecure.cspauditor.gui.CspHeadersPanel; 4 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 5 | import com.esotericsoftware.minlog.Log; 6 | 7 | import java.awt.*; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class CspTab implements IMessageEditorTab { 12 | 13 | 14 | private byte[] message; 15 | 16 | private CspHeadersPanel cspHeaders; 17 | 18 | private IExtensionHelpers helpers; 19 | private IBurpExtenderCallbacks callbacks; 20 | private IMessageEditorController controller; 21 | 22 | CspTab(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers, IMessageEditorController controller) { 23 | this.helpers = helpers; 24 | this.callbacks = callbacks; 25 | this.controller = controller; 26 | 27 | this.cspHeaders = new CspHeadersPanel(); 28 | } 29 | 30 | @Override 31 | public String getTabCaption() { 32 | return "CSP"; 33 | } 34 | 35 | @Override 36 | public Component getUiComponent() { 37 | return cspHeaders.getComponent(); 38 | } 39 | 40 | @Override 41 | public boolean isEnabled(byte[] respBytes, boolean isRequest) { 42 | if (isRequest) { 43 | return false; 44 | } else { //The tab will appears if it has at least one CSP header 45 | IResponseInfo responseInfo = helpers.analyzeResponse(respBytes); 46 | 47 | Map cspHeaders = BurpPolicyBuilder.getCspHeader(responseInfo); 48 | return cspHeaders.size() > 0; 49 | } 50 | } 51 | 52 | @Override 53 | public void setMessage(byte[] respBytes, boolean isRequest) { 54 | this.message = respBytes; 55 | 56 | try { 57 | IResponseInfo responseInfo = helpers.analyzeResponse(respBytes); 58 | List p = BurpPolicyBuilder.buildFromResponse(responseInfo); 59 | cspHeaders.displayPolicy(p); 60 | } catch (Exception e) { 61 | Log.error(e.getMessage()); 62 | } 63 | } 64 | 65 | @Override 66 | public byte[] getMessage() { 67 | return message; 68 | } 69 | 70 | @Override 71 | public boolean isModified() { 72 | return false; 73 | } 74 | 75 | @Override 76 | public byte[] getSelectedData() { 77 | return message; 78 | } 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/scanner/BurpCspIssue.java: -------------------------------------------------------------------------------- 1 | package burp.scanner; 2 | 3 | import burp.IHttpRequestResponse; 4 | import burp.IHttpService; 5 | import burp.IScanIssue; 6 | 7 | import java.net.URL; 8 | 9 | public class BurpCspIssue implements IScanIssue { 10 | 11 | private IHttpService httpService; 12 | private URL url; 13 | private IHttpRequestResponse httpMessage; 14 | private String name; 15 | private String detail; 16 | private String severity; 17 | private String confidence; 18 | 19 | 20 | public BurpCspIssue(IHttpService httpService, URL url, IHttpRequestResponse httpMessage, String name, // 21 | String detail, String severity,String confidence) { 22 | this.url = url; 23 | this.name = name; 24 | this.detail = detail; 25 | this.severity = severity; 26 | this.httpService = httpService; 27 | this.httpMessage = httpMessage; 28 | this.confidence = confidence; 29 | } 30 | 31 | @Override 32 | public URL getUrl() { 33 | return url; 34 | } 35 | 36 | @Override 37 | public String getIssueName() { 38 | return name; 39 | } 40 | 41 | @Override 42 | public int getIssueType() { 43 | return 0; 44 | } 45 | 46 | @Override 47 | public String getSeverity() { 48 | return severity; 49 | } 50 | 51 | @Override 52 | public String getConfidence() { 53 | return confidence; 54 | } 55 | 56 | @Override 57 | public String getIssueBackground() { 58 | return null; 59 | } 60 | 61 | @Override 62 | public String getRemediationBackground() { 63 | return null; 64 | } 65 | 66 | @Override 67 | public String getIssueDetail() { 68 | return detail; 69 | } 70 | 71 | @Override 72 | public String getRemediationDetail() { 73 | return null; 74 | } 75 | 76 | @Override 77 | public IHttpRequestResponse[] getHttpMessages() { 78 | return new IHttpRequestResponse[] {httpMessage}; 79 | } 80 | 81 | @Override 82 | public IHttpService getHttpService() { 83 | return httpService; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/scanner/CspHeaderScanner.java: -------------------------------------------------------------------------------- 1 | package burp.scanner; 2 | 3 | import burp.BurpPolicyBuilder; 4 | import burp.IExtensionHelpers; 5 | import burp.IHttpRequestResponse; 6 | import burp.IRequestInfo; 7 | import burp.IResponseInfo; 8 | import burp.IScanIssue; 9 | import burp.IScannerCheck; 10 | import burp.IScannerInsertionPoint; 11 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 12 | import ca.gosecure.cspauditor.model.CspIssue; 13 | import ca.gosecure.cspauditor.model.HeaderValidation; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashSet; 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | public class CspHeaderScanner implements IScannerCheck { 21 | 22 | private IExtensionHelpers helpers; 23 | 24 | public CspHeaderScanner(IExtensionHelpers helpers) { 25 | this.helpers = helpers; 26 | } 27 | 28 | @Override 29 | public List doPassiveScan(IHttpRequestResponse baseRequestResponse) { 30 | // IRequestInfo requestInfo = helpers.analyzeRequest(baseRequestResponse.getRequest()); 31 | IResponseInfo responseInfo = helpers.analyzeResponse(baseRequestResponse.getResponse()); 32 | 33 | List csp = BurpPolicyBuilder.buildFromResponse(responseInfo); 34 | 35 | List cspIssues = HeaderValidation.validateCspConfig(csp); 36 | 37 | if(cspIssues.size() == 0) 38 | return new ArrayList(); 39 | 40 | return convertIssues(cspIssues,baseRequestResponse); 41 | } 42 | 43 | @Override 44 | public List doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint) { 45 | return null; //No active scanning done 46 | } 47 | 48 | @Override 49 | public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) { 50 | if(existingIssue.getUrl().equals(newIssue.getUrl())) { 51 | return -1; //Keep the old issue 52 | } 53 | return 0; //Accept both 54 | } 55 | 56 | private List convertIssues(List issues,IHttpRequestResponse baseRequestResponse) { 57 | 58 | IRequestInfo reqInfo = helpers.analyzeRequest(baseRequestResponse.getHttpService(), baseRequestResponse.getRequest()); 59 | 60 | List burpIssues = new ArrayList<>(); 61 | Set types = new HashSet<>(); //Avoid issuing multiples alert for the same type. 62 | for(CspIssue i : issues) { 63 | if(!types.contains(i.getMessage())) { 64 | types.add(i.getMessage()); 65 | 66 | String name = "CSP: "+i.getTitle(); 67 | String detail = i.getLocalizedMessage(); 68 | String severity; 69 | //See IScanIssue.getSeverity() doc for more info 70 | if(i.getSeverity() == CspIssue.HIGH) { 71 | severity = "High"; 72 | } 73 | else if(i.getSeverity() == CspIssue.MED) { 74 | severity = "Medium"; 75 | } 76 | else if(i.getSeverity() == CspIssue.LOW) { 77 | severity = "Low"; 78 | } 79 | else { 80 | continue; 81 | } 82 | String confidence = "Firm"; 83 | 84 | 85 | burpIssues.add(new BurpCspIssue( 86 | baseRequestResponse.getHttpService(), 87 | reqInfo.getUrl(),new MockHttpRequestResponse(baseRequestResponse,i.getHighlightedValue()), 88 | name,detail,severity,confidence)); 89 | } 90 | } 91 | return burpIssues; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/scanner/MockHttpRequestResponse.java: -------------------------------------------------------------------------------- 1 | package burp.scanner; 2 | 3 | import burp.IHttpRequestResponse; 4 | import burp.IHttpRequestResponseWithMarkers; 5 | import burp.IHttpService; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class MockHttpRequestResponse implements IHttpRequestResponseWithMarkers { 11 | 12 | IHttpRequestResponse actual; 13 | List responseMarkers = new ArrayList<>(); 14 | 15 | MockHttpRequestResponse(IHttpRequestResponse actual, String... highlightedValues) { 16 | byte[] responseBytes = actual.getResponse(); 17 | for(String value : highlightedValues) { 18 | int startIndex = indexOf(responseBytes,value.getBytes()); 19 | if(startIndex != -1) { 20 | int endIndex = value.length(); 21 | responseMarkers.add(new int[] {startIndex,startIndex+endIndex}); 22 | } 23 | } 24 | this.actual = actual; 25 | } 26 | 27 | @Override 28 | public byte[] getRequest() { 29 | return actual.getRequest(); 30 | } 31 | 32 | @Override 33 | public void setRequest(byte[] message) { 34 | actual.setRequest(message); 35 | } 36 | 37 | @Override 38 | public byte[] getResponse() { 39 | return actual.getResponse(); 40 | } 41 | 42 | @Override 43 | public void setResponse(byte[] message) { 44 | actual.setResponse(message); 45 | } 46 | 47 | @Override 48 | public String getComment() { 49 | return actual.getComment(); 50 | } 51 | 52 | @Override 53 | public void setComment(String comment) { 54 | actual.setComment(comment); 55 | } 56 | 57 | @Override 58 | public String getHighlight() { 59 | return actual.getHighlight(); 60 | } 61 | 62 | @Override 63 | public void setHighlight(String color) { 64 | actual.setHighlight(color); 65 | } 66 | 67 | @Override 68 | public IHttpService getHttpService() { 69 | return actual.getHttpService(); 70 | } 71 | 72 | @Override 73 | public void setHttpService(IHttpService httpService) { 74 | actual.setHttpService(httpService); 75 | } 76 | 77 | @Override 78 | public List getRequestMarkers() { 79 | return null; 80 | } 81 | 82 | @Override 83 | public List getResponseMarkers() { 84 | return responseMarkers; 85 | } 86 | 87 | 88 | public int indexOf(byte[] outerArray, byte[] smallerArray) { 89 | for(int i = 0; i < outerArray.length - smallerArray.length+1; ++i) { 90 | boolean found = true; 91 | for(int j = 0; j < smallerArray.length; ++j) { 92 | if (outerArray[i+j] != smallerArray[j]) { 93 | found = false; 94 | break; 95 | } 96 | } 97 | if (found) return i; 98 | } 99 | return -1; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/tab/ConfigurationHelperTab.java: -------------------------------------------------------------------------------- 1 | package burp.tab; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IExtensionHelpers; 5 | import burp.IHttpRequestResponse; 6 | import burp.IHttpService; 7 | import burp.IMessageEditor; 8 | import burp.IMessageEditorController; 9 | import burp.IRequestInfo; 10 | import burp.IResponseInfo; 11 | import burp.ITab; 12 | import ca.gosecure.cspauditor.gui.generator.CspGeneratorPanel; 13 | import ca.gosecure.cspauditor.gui.generator.CspGeneratorPanelController; 14 | 15 | import java.awt.*; 16 | import java.net.MalformedURLException; 17 | import java.net.URL; 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import java.util.SortedSet; 22 | import java.util.TreeSet; 23 | 24 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 25 | import ca.gosecure.cspauditor.model.generator.DetectInlineJavascript; 26 | import com.esotericsoftware.minlog.Log; 27 | import org.json.JSONException; 28 | import org.json.JSONObject; 29 | 30 | import javax.swing.*; 31 | 32 | /** 33 | * Tab that contains three parts : 34 | * - Configuration 35 | * - External Resources 36 | * - Inline Scripts 37 | */ 38 | public class ConfigurationHelperTab implements ITab, CspGeneratorPanelController { 39 | 40 | private CspGeneratorPanel panel; 41 | 42 | private IBurpExtenderCallbacks callbacks; 43 | private IExtensionHelpers helpers; 44 | 45 | private RequestResponsePanel resourceReqRespTab; 46 | private RequestResponsePanel inlineReqRespTab; 47 | private RequestResponsePanel reportReqRespTab; 48 | 49 | public ConfigurationHelperTab(final IBurpExtenderCallbacks callbacks) { 50 | 51 | this.callbacks = callbacks; 52 | this.helpers = callbacks.getHelpers(); 53 | 54 | panel = new CspGeneratorPanel(this); 55 | panel.init(); 56 | resourceReqRespTab = new RequestResponsePanel(callbacks); 57 | panel.setResourceItem(resourceReqRespTab); 58 | inlineReqRespTab = new RequestResponsePanel(callbacks); 59 | panel.setInlineItem(inlineReqRespTab); 60 | reportReqRespTab = new RequestResponsePanel(callbacks); 61 | panel.setReportItem(reportReqRespTab); 62 | } 63 | 64 | @Override 65 | public String getTabCaption() { 66 | return "CSP"; 67 | } 68 | 69 | @Override 70 | public Component getUiComponent() { 71 | return panel.getRootPanel(); 72 | } 73 | 74 | @Override 75 | public void analyzeDomain(String domain) { 76 | Log.debug("Analyzing domain " + domain); 77 | AnalyzeDomainTask task = new AnalyzeDomainTask(domain); 78 | task.execute(); 79 | } 80 | 81 | protected class AnalyzeDomainTask extends SwingWorker { 82 | 83 | String domain; 84 | 85 | public AnalyzeDomainTask(String domain) { 86 | this.domain = domain; 87 | } 88 | 89 | @Override 90 | public Void doInBackground() { 91 | IHttpRequestResponse[] reqResponses = callbacks.getProxyHistory(); 92 | 93 | ContentSecurityPolicy csp = new ContentSecurityPolicy("CSP"); 94 | csp.addDirectiveValue("default-src","'self'"); 95 | try { 96 | URL domainSelected = new URL(domain); 97 | Log.debug("Analysing domain " + domain); 98 | panel.clearResources(); 99 | panel.clearInlineScript(); 100 | panel.clearReports(); 101 | int id = 0; 102 | for (IHttpRequestResponse reqResp : reqResponses) { 103 | id++; 104 | //Log.debug("Request "+id); 105 | IRequestInfo reqInfo = helpers.analyzeRequest(reqResp.getHttpService(), reqResp.getRequest()); 106 | if (reqResp.getResponse() == null) continue; 107 | IResponseInfo respInfo = helpers.analyzeResponse(reqResp.getResponse()); 108 | 109 | String mimeType = respInfo.getStatedMimeType() != "" ? respInfo.getStatedMimeType().toUpperCase() : respInfo.getInferredMimeType().toUpperCase(); //Uppercase is applied because to make the content-type uniform 110 | URL urlRequested = reqInfo.getUrl(); 111 | String urlString = getUrl(reqInfo); 112 | String protoAndHost = urlRequested.getProtocol() + "://" + urlRequested.getHost(); 113 | 114 | if("".equals(mimeType) || "TEXT".equals(mimeType)) { 115 | String pathSegment = urlRequested.getPath(); 116 | if(pathSegment.contains(".")) { 117 | //No content-type is returned when the server return a Not Modified response 118 | String extension = pathSegment.substring(pathSegment.lastIndexOf(".")).replace(".","").toUpperCase(); 119 | mimeType = extension; 120 | } 121 | } 122 | if("TXT".equals(mimeType)) { 123 | mimeType = "TEXT"; 124 | } 125 | if("JS".equals(mimeType)) { 126 | mimeType = "SCRIPT"; 127 | } 128 | if("JPG".equals(mimeType)) { 129 | mimeType = "JPEG"; 130 | } 131 | 132 | 133 | boolean isRequestToDomain = protoAndHost.equals(domain); 134 | 135 | //Finding inline script 136 | 137 | String host = getHeader("host", reqInfo.getHeaders()); 138 | if (isRequestToDomain) {//Same-Origin 139 | if (mimeType.equals("HTML")) { 140 | List problemInline = DetectInlineJavascript.getInstance().findInlineJs(new String(reqResp.getResponse())); 141 | 142 | for (String line : problemInline) 143 | panel.addInlineScript(String.valueOf(id), urlString, line); 144 | } 145 | } 146 | 147 | 148 | //Finding external resources 149 | 150 | if (!isRequestToDomain) {//Different-Origin 151 | 152 | String referrer = getHeader("referer", reqInfo.getHeaders()); 153 | if (referrer.startsWith("http://") || referrer.startsWith("https://")) { //Just to make sure the URL will be parsable 154 | URL referrerUrl = new URL(referrer); 155 | if (domainSelected.getHost().equals(referrerUrl.getHost())) { 156 | panel.addResource(String.valueOf(id), urlString, mimeType); 157 | mimeTypeToDirective(mimeType, protoAndHost, csp); 158 | 159 | } 160 | } 161 | } 162 | 163 | byte[] completeRequest = reqResp.getRequest(); 164 | int startOffset = reqInfo.getBodyOffset(); 165 | if(completeRequest.length - startOffset != 0) { //Skip GET request 166 | byte[] part = Arrays.copyOfRange(completeRequest, startOffset, completeRequest.length); 167 | String body = new String(part); 168 | if (body.contains("{\"csp-report\":{")) { 169 | try { 170 | JSONObject rootJson = new JSONObject(body); 171 | String documentUri = rootJson.getJSONObject("csp-report").getString("document-uri"); 172 | String originalPolicy = rootJson.getJSONObject("csp-report").getString("original-policy"); 173 | // chrome sends just the directive. firefox sends directive + sources. e.g. script-src https://domain.com ... 174 | String violatedDirective = rootJson.getJSONObject("csp-report").getString("violated-directive").split(" ")[0]; 175 | String blockedUri; 176 | 177 | if (violatedDirective.equalsIgnoreCase("frame-ancestors")) { 178 | // the report's blocked-uri is the page that got framed, not the one that needs to be added to the policy. 179 | blockedUri = rootJson.getJSONObject("csp-report").getString("referrer").split(" ")[0]; 180 | if (blockedUri.isEmpty()) 181 | continue; // browsers won't always send referrer, in which case we can't use the report. 182 | } 183 | else{ 184 | blockedUri = rootJson.getJSONObject("csp-report").getString("blocked-uri"); 185 | } 186 | 187 | String newSrc = blockedUri; 188 | try { 189 | URL url = new URL(blockedUri); 190 | String port = url.getPort() == -1 ? "" : ":" + Integer.toString(url.getPort()); 191 | newSrc = url.getProtocol() + "://" + url.getHost() + port; 192 | } 193 | catch (MalformedURLException e) { 194 | if (blockedUri.equalsIgnoreCase("inline") 195 | || blockedUri .equalsIgnoreCase("eval")){ 196 | newSrc = "'unsafe-" + blockedUri + "'"; 197 | } 198 | else if (blockedUri.equalsIgnoreCase("data") || blockedUri.equalsIgnoreCase("blob")){ 199 | newSrc = blockedUri + ":"; 200 | } 201 | else { 202 | Log.error("Invalid blocked uri", blockedUri); 203 | } 204 | } 205 | 206 | if (newSrc.equalsIgnoreCase(domain)) { 207 | newSrc = "'self'"; 208 | } 209 | 210 | csp.addDirectiveValue(violatedDirective, newSrc); 211 | panel.addReport(String.valueOf(id), blockedUri, documentUri, originalPolicy, violatedDirective); 212 | } 213 | catch (JSONException e){ //Invalid csp-report 214 | Log.error("Invalid CSP report at "+urlString); 215 | } 216 | } 217 | } 218 | 219 | } 220 | } catch (Exception e) { 221 | Log.error(e.getMessage(),e); 222 | } 223 | 224 | csp.addDirectiveValue("report-uri", "/change-this-uri/"); 225 | displayConfiguration(csp); 226 | Log.debug("Done analyzing "+domain); 227 | 228 | return null; 229 | } 230 | 231 | } 232 | 233 | private void mimeTypeToDirective(String mimeType,String host,ContentSecurityPolicy csp) { 234 | 235 | String directive = null; 236 | switch (mimeType) { 237 | case "CSS": 238 | directive = "style-src"; 239 | break; 240 | case "PNG": 241 | case "JPG": 242 | case "JPEG": 243 | case "GIF": 244 | case "SVG": 245 | case "WEBP": 246 | directive = "img-src"; 247 | break; 248 | case "SCRIPT": 249 | directive = "script-src"; 250 | break; 251 | case "FONT": 252 | directive = "font-src"; 253 | break; 254 | case "WAV": 255 | case "MP3": 256 | case "MPG": 257 | case "MPEG": 258 | case "AVI": 259 | directive = "media-src"; 260 | break; 261 | default: 262 | //Log.debug("Unknown MimeType "+mimeType); 263 | } 264 | 265 | if(directive != null) { 266 | csp.addDirectiveValue(directive, host); 267 | if(host.equals("https://fonts.googleapis.com")) { 268 | csp.addDirectiveValue("style-src", "https://fonts.gstatic.com"); 269 | csp.addDirectiveValue("font-src", "https://fonts.gstatic.com"); 270 | } 271 | } 272 | 273 | } 274 | 275 | @Override 276 | public void refreshDomains() { 277 | Log.debug("Refreshing the domain list"); 278 | DomainRefreshTask task = new DomainRefreshTask(); 279 | task.execute(); 280 | } 281 | 282 | protected class DomainRefreshTask extends SwingWorker, String> { 283 | 284 | SortedSet hosts = new TreeSet<>(); 285 | 286 | @Override 287 | public SortedSet doInBackground() { 288 | IHttpRequestResponse[] reqResponses = callbacks.getProxyHistory(); 289 | hosts = new TreeSet<>(); 290 | for (IHttpRequestResponse reqResp : reqResponses) { 291 | if ( !isCancelled() ) { 292 | IRequestInfo reqInfo = helpers.analyzeRequest(reqResp.getHttpService(), reqResp.getRequest()); 293 | String domain = reqInfo.getUrl().getProtocol() + "://" + reqInfo.getUrl().getHost(); 294 | hosts.add(domain); 295 | publish(domain); 296 | } 297 | } 298 | 299 | return hosts; 300 | } 301 | 302 | @Override 303 | public void process(List domains) { 304 | for (String domain : domains) { 305 | panel.addDomain(domain); 306 | } 307 | } 308 | 309 | } 310 | 311 | @Override 312 | public void selectResource(String url) { 313 | displayReqResp(url, resourceReqRespTab); 314 | } 315 | 316 | @Override 317 | public void selectInline(String id) { 318 | inlineReqRespTab.selectResponse(); 319 | displayReqResp(id, inlineReqRespTab); 320 | } 321 | 322 | @Override 323 | public void selectReport(String id) { 324 | displayReqResp(id, reportReqRespTab); 325 | } 326 | 327 | private void displayReqResp(String id, RequestResponsePanel tabbedPane) { 328 | //IHttpRequestResponse[] reqResponses = callbacks.getSiteMap(url); 329 | try { 330 | Integer requestId = Integer.parseInt(id); 331 | 332 | IHttpRequestResponse reqResp = callbacks.getProxyHistory()[requestId-1]; 333 | if (reqResp != null) { 334 | 335 | tabbedPane.editorRequest.setMessage(reqResp.getRequest(), true); 336 | if (reqResp.getResponse() != null) tabbedPane.editorResponse.setMessage(reqResp.getResponse(), false); 337 | 338 | } else { 339 | Log.error("Oups request not found."); 340 | } 341 | } 342 | catch (NumberFormatException | IndexOutOfBoundsException e) { 343 | 344 | } 345 | 346 | } 347 | 348 | private void displayConfiguration(ContentSecurityPolicy policy) { 349 | 350 | StringBuilder str = new StringBuilder(); 351 | str.append("HTTP/1.1 200 OK\r\n"); //Only to have a valid response so that the "new" syntax highlight is effective. 352 | str.append("Content-Security-Policy: "); 353 | str.append(policy.toHeaderString()); 354 | str.append("\r\n\r\n"); 355 | 356 | IMessageEditor msg = callbacks.createMessageEditor(null, true); 357 | 358 | msg.setMessage(str.toString().getBytes(), false); 359 | 360 | panel.setConfiguration(msg.getComponent()); 361 | } 362 | 363 | private String getUrl(IRequestInfo reqInfo) { 364 | String url = reqInfo.getUrl().toString(); 365 | 366 | try {//BUG: BurpSuite does not support default port being specified to getSiteMap() API 367 | URL urlTest = new URL(url); // Test if for port to remove from url 368 | if (urlTest.getDefaultPort() == urlTest.getPort()) { 369 | url = urlTest.getProtocol() + "://" + urlTest.getHost() + urlTest.getPath(); 370 | if (urlTest.getQuery() != null) { 371 | url += "?" + urlTest.getQuery(); 372 | } 373 | } 374 | } catch (MalformedURLException e) { 375 | } 376 | 377 | return url; 378 | } 379 | 380 | private String getHeader(String name, List headers) { 381 | for (String h : headers) { 382 | String[] parts = h.split(":", 2); 383 | String headerName = parts[0].trim(); 384 | if (headerName.equalsIgnoreCase(name) && parts.length > 1) { 385 | return parts[1].trim(); 386 | } 387 | } 388 | return ""; 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/tab/RequestResponsePanel.java: -------------------------------------------------------------------------------- 1 | package burp.tab; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IMessageEditor; 5 | 6 | import javax.swing.*; 7 | 8 | public class RequestResponsePanel extends JTabbedPane { 9 | 10 | public IMessageEditor editorRequest; 11 | public IMessageEditor editorResponse; 12 | 13 | public RequestResponsePanel(IBurpExtenderCallbacks callbacks) { 14 | 15 | this.editorRequest = callbacks.createMessageEditor(null, false); 16 | this.editorResponse = callbacks.createMessageEditor(null, false); 17 | 18 | addTab("Request", editorRequest.getComponent()); 19 | addTab("Response", editorResponse.getComponent()); 20 | } 21 | 22 | public void selectResponse() { 23 | setSelectedIndex(1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /csp-auditor-burp-plugin/src/main/java/burp/tab/StaticMessageEditor.java: -------------------------------------------------------------------------------- 1 | package burp.tab; 2 | 3 | import burp.IHttpService; 4 | import burp.IMessageEditor; 5 | import burp.IMessageEditorController; 6 | 7 | import java.awt.*; 8 | 9 | public class StaticMessageEditor implements IMessageEditorController { 10 | private IHttpService iHttpService; 11 | private byte[] request; 12 | private byte[] response; 13 | 14 | public StaticMessageEditor(IHttpService iHttpService, byte[] request, byte[] response) { 15 | this.iHttpService = iHttpService; 16 | this.request = request; 17 | this.response = response; 18 | } 19 | 20 | @Override 21 | public IHttpService getHttpService() { 22 | return iHttpService; 23 | } 24 | 25 | @Override 26 | public byte[] getRequest() { 27 | return request; 28 | } 29 | 30 | @Override 31 | public byte[] getResponse() { 32 | return response; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /csp-auditor-core/build.gradle: -------------------------------------------------------------------------------- 1 | group 'csp-auditor' 2 | version '1.0-SNAPSHOT' 3 | 4 | 5 | 6 | dependencies { 7 | compile group: 'com.esotericsoftware', name: 'minlog', version: '1.3' 8 | compile group: 'commons-io', name: 'commons-io', version: '2.4' 9 | compile group: 'org.json', name: 'json', version: '20140107' 10 | testCompile group: 'org.testng', name: 'testng', version: '6.8.8' 11 | 12 | //files('$ROOT_DIR$/lib/forms_rt-12.1.8.jar') 13 | //files('$ROOT_DIR$/lib/javac2-12.1.8.jar') 14 | } 15 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/CspHeadersPanel.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui; 2 | 3 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 4 | import ca.gosecure.cspauditor.model.Directive; 5 | import ca.gosecure.cspauditor.model.HeaderValidation; 6 | import com.esotericsoftware.minlog.Log; 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.net.URL; 16 | 17 | public class CspHeadersPanel { 18 | 19 | 20 | private JPanel mainPanel = new JPanel(); 21 | 22 | public CspHeadersPanel() { 23 | mainPanel.setLayout(new BorderLayout()); 24 | } 25 | 26 | public JComponent getComponent() { 27 | return mainPanel; 28 | } 29 | 30 | private static final URL ICON_HIGH = getAccessibleResource("/resources/Media/scan_issue_high_certain_rpt.png"); 31 | private static final URL ICON_MED = getAccessibleResource("/resources/Media/scan_issue_medium_certain_rpt.png"); 32 | private static final URL ICON_LOW = getAccessibleResource("/resources/Media/scan_issue_decoration_info_certain.png"); 33 | 34 | public void displayPolicy(java.util.List p) { 35 | 36 | StringBuilder str = new StringBuilder(); 37 | 38 | str.append(""); 39 | for(ContentSecurityPolicy policyOrig : p) { 40 | ContentSecurityPolicy policy = policyOrig.getComputedPolicy(); 41 | str.append("

Header : " + policy.getHeaderName() + "

\n"); 42 | 43 | for (Directive d : policy.getDirectives().values()) { 44 | str.append("
  " + d.getName() + " " + (d.isImplicit() ? "(Implicit taken from the default-src)" : "") + "
\n"); 45 | 46 | for (String value : d.getValues()) { 47 | if (HeaderValidation.isAllowingAnyScript(d.getName(),value) || 48 | HeaderValidation.isAllowingInlineScript(d.getName(),value) || 49 | HeaderValidation.isAllowingUnsafeEvalScript(d.getName(),value) || 50 | HeaderValidation.isUserContentHost(d.getName(),value) || 51 | HeaderValidation.isHostingVulnerableJs(d.getName(),value)) { 52 | str.append(iconify(value,ICON_HIGH,"red")); 53 | 54 | } else if (HeaderValidation.isAllowingAnyStyle(d.getName(),value) || 55 | HeaderValidation.isAllowingAny(d.getName(),value)) { 56 | str.append(iconify(value,ICON_MED,"orange")); 57 | 58 | } else { 59 | str.append(iconify(value,ICON_LOW,"")); 60 | } 61 | } 62 | } 63 | } 64 | str.append("

"); 65 | 66 | JLabel lbl = new JLabel(); 67 | lbl.setText(str.toString()); 68 | 69 | 70 | mainPanel.removeAll(); 71 | 72 | mainPanel.add(new JScrollPane(lbl)); 73 | } 74 | 75 | private static String iconify(String message,URL icon,String color) { 76 | StringBuilder buffer = new StringBuilder("      "); 77 | 78 | buffer.append(""); 79 | if(icon != null) { 80 | buffer.append(""); 81 | } 82 | else { 83 | buffer.append(" - "); 84 | } 85 | 86 | buffer.append(" "); 87 | buffer.append(message); 88 | 89 | // if(icon != null) { 90 | buffer.append("
\n"); 91 | // } 92 | // else { 93 | // buffer.append(""); 94 | // } 95 | buffer.append("
"); 96 | return buffer.toString(); 97 | } 98 | 99 | private static URL getAccessibleResource(String url) { 100 | 101 | URL urlResource = CspHeadersPanel.class.getResource(url); 102 | if(urlResource == null) { 103 | Log.error("Resource not found "+url); 104 | return null; 105 | } 106 | 107 | if(!urlResource.getFile().contains(".jar!") && !urlResource.getFile().contains(".zap!")) 108 | { 109 | return urlResource; 110 | } 111 | 112 | try { 113 | File tempFile = File.createTempFile("temp-file-name", ".png"); 114 | 115 | System.out.println(tempFile.toPath()); 116 | 117 | try (InputStream in = urlResource.openStream(); FileOutputStream out = new FileOutputStream(tempFile)) { 118 | IOUtils.copy(in, out); 119 | } 120 | return tempFile.toURI().toURL(); 121 | } 122 | catch (IOException e) { 123 | Log.warn(e.getMessage()); 124 | return null; 125 | } 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/CspGeneratorPanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 |
216 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/CspGeneratorPanel.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui.generator; 2 | 3 | import com.esotericsoftware.minlog.Log; 4 | import main.java.ca.gosecure.cspauditor.gui.generator.SortedUniqueComboBoxModel; 5 | 6 | import javax.swing.*; 7 | import javax.swing.event.HyperlinkEvent; 8 | import javax.swing.event.ListSelectionEvent; 9 | import javax.swing.table.DefaultTableModel; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.util.Collection; 14 | import java.util.Vector; 15 | 16 | public class CspGeneratorPanel { 17 | private JButton analyseButton; 18 | private JComboBox comboBox1; 19 | private JPanel rootPanel; 20 | private JButton refreshButton; 21 | private JTabbedPane resultTabbedPane; 22 | private JPanel configurationPanel; 23 | private JPanel inlineScriptPanel; 24 | private JTable resourcesTable; 25 | private JPanel resourcePanel; 26 | private JTable inlinesTable; 27 | private JPanel inlinePanel; 28 | private JPanel configTabPanel; 29 | private JTable reportsTable; 30 | private JPanel reportPanel; 31 | private JTextPane warningConfigurationTextPane; 32 | 33 | DefaultTableModel tableResourcesModel = new DefaultTableModel() { 34 | @Override 35 | public boolean isCellEditable(int row, int column) { 36 | return false; 37 | } 38 | }; 39 | DefaultTableModel tableInlinesModel = new DefaultTableModel() { 40 | @Override 41 | public boolean isCellEditable(int row, int column) { 42 | return false; 43 | } 44 | }; 45 | DefaultTableModel reportsModel = new DefaultTableModel() { 46 | @Override 47 | public boolean isCellEditable(int row, int column) { 48 | return false; 49 | } 50 | }; 51 | 52 | 53 | private CspGeneratorPanelController controller; 54 | //private CspGeneratorPanelUiProvider uiProvider; 55 | 56 | public CspGeneratorPanel(CspGeneratorPanelController controller) { 57 | this.controller = controller; 58 | //this.uiProvider = uiProvider; 59 | 60 | } 61 | 62 | public void init() { 63 | 64 | 65 | //Resources table 66 | tableResourcesModel.addColumn("id"); 67 | tableResourcesModel.addColumn("Request"); 68 | tableResourcesModel.addColumn("Type"); 69 | resourcesTable.setModel(tableResourcesModel); 70 | resourcesTable.getColumnModel().getColumn(0).setMaxWidth(45); 71 | 72 | resourcesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent event) -> { 73 | int viewRow = resourcesTable.getSelectedRow(); 74 | if(viewRow == -1) return; 75 | 76 | Vector values = (Vector) tableResourcesModel.getDataVector().get(viewRow); 77 | selectResourceItem((String) values.get(0)); 78 | }); 79 | 80 | //Inlines table 81 | tableInlinesModel.addColumn("id"); 82 | tableInlinesModel.addColumn("Request"); 83 | tableInlinesModel.addColumn("Code"); 84 | inlinesTable.setModel(tableInlinesModel); 85 | inlinesTable.getColumnModel().getColumn(0).setMaxWidth(45); 86 | 87 | inlinesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent event) -> { 88 | int viewRow = inlinesTable.getSelectedRow(); 89 | if(viewRow == -1) return; 90 | Vector values = (Vector) tableInlinesModel.getDataVector().get(viewRow); 91 | selectInlineItem((String) values.get(0)); 92 | }); 93 | 94 | 95 | //Report table 96 | reportsModel.addColumn("id"); 97 | reportsModel.addColumn("blocked-uri"); 98 | reportsModel.addColumn("document-uri"); 99 | reportsModel.addColumn("original-policy"); 100 | reportsModel.addColumn("violated-directive"); 101 | reportsTable.setModel(reportsModel); 102 | reportsTable.getColumnModel().getColumn(0).setMaxWidth(45); 103 | 104 | reportsTable.getSelectionModel().addListSelectionListener((ListSelectionEvent event) -> { 105 | int viewRow = reportsTable.getSelectedRow(); 106 | if(viewRow == -1) return; 107 | 108 | Vector values = (Vector) reportsModel.getDataVector().get(viewRow); 109 | selectReportItem((String) values.get(0)); 110 | }); 111 | 112 | //Analyze 113 | analyseButton.addActionListener((ActionEvent e) -> { 114 | String value = (String) comboBox1.getSelectedItem(); 115 | if (value != null) 116 | controller.analyzeDomain(value); 117 | }); 118 | 119 | 120 | //Refresh button 121 | this.refreshButton.setText("\u21BB"); 122 | refreshButton.addActionListener(new ActionListener() { 123 | @Override 124 | public void actionPerformed(ActionEvent e) { 125 | controller.refreshDomains(); 126 | } 127 | }); 128 | 129 | this.warningConfigurationTextPane.addHyperlinkListener(e -> { 130 | if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 131 | if (Desktop.isDesktopSupported()) { 132 | try { 133 | Desktop.getDesktop().browse(e.getURL().toURI()); 134 | } catch (Exception ex) { 135 | ex.printStackTrace(); 136 | } 137 | } else { 138 | System.out.println(e.getURL().toString()+" clicked"); 139 | } 140 | } 141 | }); 142 | } 143 | 144 | private void selectReportItem(String id) { 145 | controller.selectReport(id); 146 | } 147 | 148 | public JPanel getRootPanel() { 149 | return rootPanel; 150 | } 151 | 152 | ////Domains 153 | 154 | public void clearDomains() { 155 | comboBox1.removeAll(); 156 | } 157 | 158 | public void addDomains(Collection domains) { 159 | for (String domain : domains) { 160 | comboBox1.addItem(domain); 161 | } 162 | } 163 | 164 | public void addDomain(String domain) { 165 | comboBox1.addItem(domain); 166 | } 167 | 168 | ////Configurations 169 | 170 | public void setConfiguration(Component configuration) { 171 | configurationPanel.removeAll(); 172 | configurationPanel.add(configuration); 173 | } 174 | 175 | ////Resources 176 | 177 | public void clearResources() { 178 | tableResourcesModel.setRowCount(0); 179 | } 180 | 181 | public void addResource(String id, String url, String type) { 182 | Log.debug("Adding resource " + url); 183 | tableResourcesModel.addRow(new String[]{id, url, type}); 184 | } 185 | 186 | public void selectResourceItem(String path) { 187 | controller.selectResource(path); 188 | } 189 | 190 | public void setResourceItem(Component resource) { 191 | resourcePanel.removeAll(); 192 | resourcePanel.add(resource); 193 | } 194 | 195 | ////Inlines scripts 196 | 197 | public void clearInlineScript() { 198 | tableInlinesModel.setRowCount(0); 199 | } 200 | 201 | public void addInlineScript(String id, String urlString, String line) { 202 | Log.debug("Adding inline script from " + urlString); 203 | tableInlinesModel.addRow(new String[]{id, urlString, line}); 204 | } 205 | 206 | public void selectInlineItem(String path) { 207 | controller.selectInline(path); 208 | } 209 | 210 | public void setInlineItem(Component resource) { 211 | inlinePanel.removeAll(); 212 | inlinePanel.add(resource); 213 | } 214 | 215 | ////Reports 216 | 217 | public void setReportItem(Component resource) { 218 | reportPanel.removeAll(); 219 | reportPanel.add(resource); 220 | } 221 | 222 | 223 | public void addReport(String id, String blockedUri, String documentUri, String originalPolicy, String violatedDirective) { 224 | reportsModel.addRow(new String[]{id, blockedUri, documentUri,originalPolicy,violatedDirective}); 225 | } 226 | 227 | public void clearReports() { 228 | reportsModel.setRowCount(0); 229 | } 230 | 231 | { 232 | // GUI initializer generated by IntelliJ IDEA GUI Designer 233 | // >>> IMPORTANT!! <<< 234 | // DO NOT EDIT OR ADD ANY CODE HERE! 235 | $$$setupUI$$$(); 236 | } 237 | 238 | /** 239 | * Method generated by IntelliJ IDEA GUI Designer 240 | * >>> IMPORTANT!! <<< 241 | * DO NOT edit this method OR call it in your code! 242 | * 243 | * @noinspection ALL 244 | */ 245 | private void $$$setupUI$$$() { 246 | rootPanel = new JPanel(); 247 | rootPanel.setLayout(new BorderLayout(0, 0)); 248 | final JPanel panel1 = new JPanel(); 249 | panel1.setLayout(new BorderLayout(0, 0)); 250 | rootPanel.add(panel1, BorderLayout.CENTER); 251 | final JPanel panel2 = new JPanel(); 252 | panel2.setLayout(new BorderLayout(0, 0)); 253 | panel2.setEnabled(true); 254 | panel1.add(panel2, BorderLayout.NORTH); 255 | comboBox1 = new JComboBox(); 256 | comboBox1.setModel(new SortedUniqueComboBoxModel()); 257 | panel2.add(comboBox1, BorderLayout.CENTER); 258 | final JPanel panel3 = new JPanel(); 259 | panel3.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); 260 | panel2.add(panel3, BorderLayout.EAST); 261 | refreshButton = new JButton(); 262 | refreshButton.setText("Refresh"); 263 | panel3.add(refreshButton); 264 | analyseButton = new JButton(); 265 | analyseButton.setHideActionText(true); 266 | analyseButton.setText("Analyze"); 267 | panel3.add(analyseButton); 268 | final JPanel panel4 = new JPanel(); 269 | panel4.setLayout(new BorderLayout(0, 0)); 270 | panel1.add(panel4, BorderLayout.CENTER); 271 | resultTabbedPane = new JTabbedPane(); 272 | resultTabbedPane.setTabLayoutPolicy(0); 273 | panel4.add(resultTabbedPane, BorderLayout.CENTER); 274 | configTabPanel = new JPanel(); 275 | configTabPanel.setLayout(new BorderLayout(0, 0)); 276 | resultTabbedPane.addTab("Configuration", configTabPanel); 277 | configurationPanel = new JPanel(); 278 | configurationPanel.setLayout(new BorderLayout(0, 0)); 279 | configTabPanel.add(configurationPanel, BorderLayout.CENTER); 280 | final JPanel panel5 = new JPanel(); 281 | panel5.setLayout(new BorderLayout(0, 0)); 282 | configTabPanel.add(panel5, BorderLayout.SOUTH); 283 | warningConfigurationTextPane = new JTextPane(); 284 | warningConfigurationTextPane.setEditable(false); 285 | warningConfigurationTextPane.setContentType("text/html"); 286 | warningConfigurationTextPane.setText(" Warning: Refer to "Inline Scripts" to see scripts that are not compatible with CSP strict mode (Directive "script-src 'unsafe-inline'" is not included.).
More information on CSP pitfalls "); 287 | panel5.add(warningConfigurationTextPane, BorderLayout.CENTER); 288 | final JPanel panel6 = new JPanel(); 289 | panel6.setLayout(new BorderLayout(0, 0)); 290 | resultTabbedPane.addTab("External Resources", panel6); 291 | final JSplitPane splitPane1 = new JSplitPane(); 292 | splitPane1.setOrientation(0); 293 | panel6.add(splitPane1, BorderLayout.CENTER); 294 | resourcePanel = new JPanel(); 295 | resourcePanel.setLayout(new BorderLayout(0, 0)); 296 | splitPane1.setRightComponent(resourcePanel); 297 | final JScrollPane scrollPane1 = new JScrollPane(); 298 | splitPane1.setLeftComponent(scrollPane1); 299 | resourcesTable = new JTable(); 300 | scrollPane1.setViewportView(resourcesTable); 301 | final JPanel panel7 = new JPanel(); 302 | panel7.setLayout(new BorderLayout(0, 0)); 303 | resultTabbedPane.addTab("Inline Scripts", panel7); 304 | final JSplitPane splitPane2 = new JSplitPane(); 305 | splitPane2.setOrientation(0); 306 | panel7.add(splitPane2, BorderLayout.CENTER); 307 | final JScrollPane scrollPane2 = new JScrollPane(); 308 | splitPane2.setLeftComponent(scrollPane2); 309 | inlinesTable = new JTable(); 310 | scrollPane2.setViewportView(inlinesTable); 311 | inlinePanel = new JPanel(); 312 | inlinePanel.setLayout(new BorderLayout(0, 0)); 313 | splitPane2.setRightComponent(inlinePanel); 314 | final JPanel panel8 = new JPanel(); 315 | panel8.setLayout(new BorderLayout(0, 0)); 316 | resultTabbedPane.addTab("Reports", panel8); 317 | final JSplitPane splitPane3 = new JSplitPane(); 318 | splitPane3.setOrientation(0); 319 | panel8.add(splitPane3, BorderLayout.CENTER); 320 | final JScrollPane scrollPane3 = new JScrollPane(); 321 | splitPane3.setLeftComponent(scrollPane3); 322 | reportsTable = new JTable(); 323 | scrollPane3.setViewportView(reportsTable); 324 | reportPanel = new JPanel(); 325 | reportPanel.setLayout(new BorderLayout(0, 0)); 326 | splitPane3.setRightComponent(reportPanel); 327 | } 328 | 329 | /** 330 | * @noinspection ALL 331 | */ 332 | public JComponent $$$getRootComponent$$$() { 333 | return rootPanel; 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/CspGeneratorPanelController.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui.generator; 2 | 3 | public interface CspGeneratorPanelController { 4 | 5 | void analyzeDomain(String domain); 6 | 7 | void refreshDomains(); 8 | 9 | void selectResource(String id); 10 | 11 | void selectInline(String id); 12 | 13 | void selectReport(String id); 14 | } 15 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/CspGeneratorPanelUiProvider.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui.generator; 2 | 3 | import java.awt.*; 4 | 5 | public interface CspGeneratorPanelUiProvider { 6 | 7 | Component getTextEditor(byte[] content); 8 | 9 | // Component getRequestEditor(byte[] request,byte[] response); 10 | } 11 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/RequestResponse.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui.generator; 2 | 3 | public interface RequestResponse { 4 | } 5 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/gui/generator/SortedUniqueComboBoxModel.java: -------------------------------------------------------------------------------- 1 | package main.java.ca.gosecure.cspauditor.gui.generator; 2 | 3 | public class SortedUniqueComboBoxModel extends javax.swing.DefaultComboBoxModel { 4 | 5 | public SortedUniqueComboBoxModel() { 6 | super(); 7 | } 8 | 9 | @Override 10 | public void addElement(Object element) { 11 | insertElementAt(element, 0); 12 | } 13 | 14 | @Override 15 | public void insertElementAt(Object element, int index) { 16 | int size = getSize(); 17 | for (index = 0; index < size; index++) { 18 | Comparable c = (Comparable) getElementAt(index); 19 | int comparison = c.compareTo(element); 20 | if (comparison == 0) return; 21 | if (comparison > 0) { 22 | break; 23 | } 24 | } 25 | super.insertElementAt(element, index); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/ContentSecurityPolicy.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.LinkedHashMap; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | public class ContentSecurityPolicy { 12 | 13 | private String headerName; 14 | private Map directives = new LinkedHashMap<>(); 15 | 16 | private Set importantDirectives = new HashSet<>(Arrays.asList("script-src","object-src","style-src","img-src","media-src","frame-src","font-src","connect-src")); 17 | 18 | 19 | public ContentSecurityPolicy(String headerName) { 20 | this.headerName = headerName; 21 | } 22 | 23 | public void addDirective(Directive d) { 24 | directives.put(d.getName(),d); 25 | } 26 | 27 | 28 | public String getHeaderName() { 29 | return headerName; 30 | } 31 | 32 | public Map getDirectives() { 33 | return directives; 34 | } 35 | 36 | public ContentSecurityPolicy getComputedPolicy() { 37 | ContentSecurityPolicy pol = new ContentSecurityPolicy(headerName); 38 | 39 | Directive defaultSrc = directives.get("default-src"); 40 | 41 | if(defaultSrc == null) { 42 | defaultSrc = new Directive("default-src", Arrays.asList("'self'"), true); 43 | } 44 | else { 45 | pol.addDirective(defaultSrc); 46 | } 47 | 48 | //Display the important first 49 | for(String directive : importantDirectives) { 50 | pol.addDirective(getDirectiveOrDefault(directive ,defaultSrc)); 51 | } 52 | 53 | //Other directive found.. 54 | for(String otherDirName : directives.keySet()) { 55 | if(!importantDirectives.contains(otherDirName)) { 56 | pol.addDirective(directives.get(otherDirName)); 57 | } 58 | } 59 | 60 | return pol; 61 | } 62 | 63 | private Directive getDirectiveOrDefault(String name,Directive defaultSrc) { 64 | Directive targetDir = directives.get(name); 65 | return targetDir != null ? targetDir.clone(name) : defaultSrc.cloneImplicit(name); 66 | } 67 | 68 | public String toHeaderString() { 69 | StringBuilder str = new StringBuilder(); 70 | for(String key : directives.keySet()) { 71 | str.append(key); 72 | Directive d = directives.get(key); 73 | for(String val : d.getValues()) { 74 | str.append(' ').append(val); 75 | } 76 | str.append("; "); 77 | } 78 | return str.toString(); 79 | } 80 | 81 | public String toString() { 82 | StringBuilder str = new StringBuilder(); 83 | 84 | ContentSecurityPolicy policy = getComputedPolicy(); 85 | str.append("Header : "+policy.headerName+"\n"); 86 | 87 | for(Directive d : policy.directives.values()) { 88 | str.append(d.getName()+""+(d.isImplicit()? " (Implicit)" : "")+"\n"); 89 | for(String value : d.getValues()) { 90 | str.append("\t- "+value+"\n"); 91 | } 92 | } 93 | return str.toString(); 94 | } 95 | 96 | public void addDirectiveValue(String key, String domain) { 97 | Directive d = directives.get(key); 98 | if(d == null) { 99 | Directive newD = new Directive(key, new ArrayList<>()); 100 | directives.put(key, newD); 101 | d = newD; 102 | } 103 | if(!d.getValues().contains(domain)) { 104 | d.getValues().add(domain); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/CspIssue.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import java.io.InputStream; 4 | 5 | public class CspIssue { 6 | public static final int HIGH = 2; 7 | public static final int MED = 1; 8 | public static final int LOW = 0; 9 | public static final int INFO = -1; 10 | 11 | private final int severity; 12 | private final String title; 13 | private final String message; 14 | private final Directive directive; 15 | private final String highlightedValue; 16 | 17 | public CspIssue(int severity, String title, String message, Directive directive,String highlightValue) { 18 | this.severity = severity; 19 | this.title = title; 20 | this.message = message; 21 | this.directive = directive; 22 | this.highlightedValue = highlightValue; 23 | } 24 | 25 | public int getSeverity() { 26 | return severity; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | 33 | public String getMessage() { 34 | return message; 35 | } 36 | 37 | public Directive getDirective() { 38 | return directive; 39 | } 40 | 41 | public String getHighlightedValue() { 42 | return highlightedValue; 43 | } 44 | 45 | public String getLocalizedMessage() { 46 | InputStream in = getClass().getResourceAsStream("/resources/descriptions/"+message+".htm"); 47 | if(in == null) { 48 | return "Localized message not found :("; 49 | } 50 | 51 | String description = convertStreamToString(in); 52 | 53 | if(directive != null) { 54 | return description + "\nWeak configuration
\n" + 55 | "
"+directive.getName()+": "+highlightedValue+"
\n" + 56 | "
"; 57 | } 58 | else { 59 | return description + "\nWeak configuration
\n" + 60 | "
"+highlightedValue+"
\n" + 61 | "
"; 62 | } 63 | } 64 | 65 | private static String convertStreamToString(InputStream is) { 66 | java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); 67 | return s.hasNext() ? s.next() : ""; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | String sev = severity == HIGH ? "High" : 73 | severity == MED? "Med" : "Low"; 74 | return "[" + sev +"]\t" + message + "\t("+directive+")"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/Directive.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class Directive { 8 | private final String name; 9 | private final List values; 10 | private final boolean implicit; 11 | 12 | public Directive(String name,List values) { 13 | this.name = name; 14 | this.values = new ArrayList<>(values); 15 | this.implicit = false; 16 | } 17 | 18 | public Directive(String name,List values,boolean implicit) { 19 | this.name = name; 20 | this.values = new ArrayList<>(values); 21 | this.implicit = implicit; 22 | } 23 | 24 | //Getters 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public List getValues() { 31 | return values; 32 | } 33 | 34 | public boolean isImplicit() { 35 | return implicit; 36 | } 37 | 38 | ////Clones 39 | 40 | protected Directive clone(String name) { 41 | return new Directive(name, cloneArrayList(values), false); 42 | } 43 | protected Directive cloneImplicit(String name) { 44 | return new Directive(name, cloneArrayList(values), true); 45 | } 46 | 47 | private List cloneArrayList(List values) { 48 | List newList = new ArrayList(); 49 | newList.addAll(values); 50 | return newList; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return getName()+": "+ join("",getValues()); 56 | } 57 | 58 | /** 59 | * In replacement of String.join() from Java 8. 60 | * @param delimiter the delimiter that separates each element 61 | * @param elements the elements to join together. 62 | * @return a new String that is composed of the elements separated by the delimiter 63 | */ 64 | public static String join(String delimiter, List elements) 65 | { 66 | StringBuilder sb = new StringBuilder(); 67 | for(int i = 0; i < elements.size(); i++) 68 | { 69 | sb.append(elements.get(i)); 70 | if(i < elements.size() - 1) 71 | sb.append(delimiter); 72 | } 73 | return sb.toString(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/HeaderValidation.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class HeaderValidation { 7 | 8 | private static final String[] deprecatedHeaders = {"X-Content-Security-Policy"}; 9 | 10 | public static boolean isAllowingAnyScript(String name, String value) { 11 | return (name.equals("script-src") || name.equals("object-src")) 12 | && (value.equals("*")); 13 | } 14 | 15 | public static boolean isAllowingInlineScript(String name, String value) { 16 | return (name.equals("script-src") || name.equals("object-src")) 17 | && (value.equals("'unsafe-inline'")); 18 | } 19 | public static boolean isAllowingUnsafeEvalScript(String name, String value) { 20 | return (name.equals("script-src") || name.equals("object-src")) 21 | && (value.equals("'unsafe-eval'")); 22 | } 23 | 24 | public static boolean isAllowingAny(String name, String value) { 25 | return value.equals("'unsafe-inline'") || value.equals("'unsafe-eval'") || value.equals("*"); 26 | } 27 | 28 | public static boolean isAllowingAnyStyle(String name, String value) { 29 | return (name.equals("style-src") && value.equals("*")); 30 | } 31 | 32 | public static boolean isUserContentHost(String name, String value) { 33 | if(!(name.equals("script-src") || name.equals("object-src"))) { 34 | return false; 35 | } 36 | return WeakCdnHost.getInstance().isUserContentHost(value); 37 | } 38 | 39 | public static boolean isHostingVulnerableJs(String name, String value) { 40 | if(!(name.equals("script-src") || name.equals("object-src"))) { 41 | return false; 42 | } 43 | return WeakCdnHost.getInstance().isHostingVulnerableJs(value); 44 | } 45 | 46 | public static boolean isHeaderDeprecated(String headerName) { 47 | return !"content-security-policy".equals(headerName.toLowerCase()); 48 | } 49 | 50 | public static List validateCspConfig(List csp) { 51 | List issues = new ArrayList<>(); 52 | 53 | for(ContentSecurityPolicy policyOrig : csp) { 54 | ContentSecurityPolicy policy = policyOrig.getComputedPolicy(); 55 | 56 | if(isHeaderDeprecated(policy.getHeaderName())){ 57 | issues.add(new CspIssue(CspIssue.MED, "Deprecated header name", // 58 | "issue_deprecated_header_name",null,policy.getHeaderName())); 59 | } 60 | 61 | for (Directive d : policy.getDirectives().values()) { 62 | for (String value : d.getValues()) { 63 | if (isAllowingAnyScript(d.getName(),value)) { 64 | issues.add(new CspIssue(CspIssue.MED, "External scripts allowed", "issue_script_wildcard", d, value)); 65 | } else if (isAllowingInlineScript(d.getName(),value)) { 66 | issues.add(new CspIssue(CspIssue.MED, "Inline scripts can be inserted", "issue_script_unsafe_inline",d, value)); 67 | } else if (isAllowingUnsafeEvalScript(d.getName(),value)) { 68 | issues.add(new CspIssue(CspIssue.MED, "Libraries using eval or setTimeout are allow", "issue_script_unsafe_eval",d, value)); 69 | } else if (isAllowingAnyStyle(d.getName(),value)) { 70 | issues.add(new CspIssue(CspIssue.LOW, "External stylesheets allowed", "issue_style", d, value)); 71 | } else if (isUserContentHost(d.getName(), value)) { 72 | issues.add(new CspIssue(CspIssue.MED, "The domain is hosting user content", "issue_risky_host_user_content", d, value)); 73 | } else if (isHostingVulnerableJs(d.getName(), value)) { 74 | issues.add(new CspIssue(CspIssue.MED, "The domain is hosting vulnerable JavaScript", "issue_risky_host_known_vulnerable_js", d, value)); 75 | } else if (isAllowingAny(d.getName(),value)) { 76 | issues.add(new CspIssue(CspIssue.INFO, "Use of wildcard", "issue_wildcard_limited", d, value)); 77 | } 78 | } 79 | } 80 | } 81 | 82 | return issues; 83 | } 84 | 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/WeakCdnHost.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import ca.gosecure.cspauditor.util.SimpleListFile; 4 | import com.esotericsoftware.minlog.Log; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.util.HashSet; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | public class WeakCdnHost { 16 | 17 | private Boolean blacklistLoaded = false; 18 | 19 | private static final String USER_CONTENT_HOSTS_PATH = "/resources/data/csp_host_user_content.txt"; 20 | private static final String VULNERABLE_JS_HOSTS_PATH = "/resources/data/csp_host_vulnerable_js.txt"; 21 | 22 | private Set blacklistUserContentHosts = new HashSet<>(); 23 | private Set blacklistVulnerableJsHosts = new HashSet<>(); 24 | 25 | private static WeakCdnHost instance = new WeakCdnHost(); 26 | 27 | //Singleton pattern 28 | private WeakCdnHost() {} 29 | public static WeakCdnHost getInstance() { return instance; } 30 | 31 | 32 | private void preloadLists() { 33 | if(!blacklistLoaded) { //Race-conditions will at worst load two times the list. 34 | blacklistLoaded = true; 35 | 36 | try { 37 | loadFileToSet(USER_CONTENT_HOSTS_PATH, blacklistUserContentHosts); 38 | loadFileToSet(VULNERABLE_JS_HOSTS_PATH, blacklistVulnerableJsHosts); 39 | } catch (IOException e) { 40 | Log.error("Unable to load the blacklist hosts :"+ e.getMessage(),e); 41 | } 42 | } 43 | } 44 | 45 | private void loadFileToSet(String file,Set set) throws IOException { 46 | Log.debug("Loading file : "+file); 47 | 48 | SimpleListFile.openFile(file, (String line) -> { 49 | 50 | //Adding precise main 51 | set.add(line); 52 | 53 | //Adding subpath 54 | String subPath = line; 55 | int lastIndex = -1; 56 | while((lastIndex = subPath.lastIndexOf("/")) != -1) { 57 | subPath = subPath.substring(0, lastIndex+1); 58 | set.add(subPath); 59 | // System.out.println(subPath); 60 | subPath = subPath.substring(0, subPath.length()-1); //Remove the trailing slash 61 | } 62 | 63 | }); 64 | 65 | //Add wildcard on subdomain 66 | 67 | Iterator it = set.iterator(); 68 | Set wildcardsVariations = new HashSet<>(); 69 | while(it.hasNext()) { 70 | String url = it.next(); 71 | 72 | String subDomain = url; 73 | 74 | int firstIndex = -1; 75 | while((firstIndex = subDomain.indexOf(".")) != -1) { 76 | subDomain = subDomain.substring(firstIndex+1,subDomain.length()); 77 | wildcardsVariations.add("*."+subDomain); 78 | //System.out.println(subDomain); 79 | } 80 | } 81 | 82 | set.addAll(wildcardsVariations); 83 | 84 | } 85 | 86 | public boolean isUserContentHost(String value) { 87 | value = value.replaceFirst("http://","").replaceFirst("https://",""); 88 | preloadLists(); 89 | return blacklistUserContentHosts.contains(value) || blacklistUserContentHosts.contains(value+"/"); 90 | } 91 | 92 | 93 | public boolean isHostingVulnerableJs(String value) { 94 | value = value.replaceFirst("http://","").replaceFirst("https://",""); 95 | preloadLists(); 96 | return blacklistVulnerableJsHosts.contains(value) || blacklistVulnerableJsHosts.contains(value+"/"); 97 | } 98 | 99 | public Set getBlacklistVulnerableJsHosts() { 100 | preloadLists(); 101 | return blacklistVulnerableJsHosts; 102 | } 103 | public Set getBlacklistUserContentHosts() { 104 | preloadLists(); 105 | return blacklistUserContentHosts; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/model/generator/DetectInlineJavascript.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model.generator; 2 | 3 | import ca.gosecure.cspauditor.util.SimpleListFile; 4 | import com.esotericsoftware.minlog.Log; 5 | 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Set; 11 | import java.util.StringTokenizer; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public class DetectInlineJavascript { 16 | 17 | private Set jsInlineEvents = new HashSet<>(); 18 | 19 | private static DetectInlineJavascript instance = new DetectInlineJavascript(); 20 | 21 | //Singleton pattern 22 | private DetectInlineJavascript() {} 23 | public static DetectInlineJavascript getInstance() { return instance; } 24 | 25 | private void preloadEvents() { 26 | if(jsInlineEvents.size() < 1) { 27 | try { 28 | SimpleListFile.openFile("/resources/data/js_inline_event.txt", (String line) -> { 29 | jsInlineEvents.add(line); 30 | }); 31 | } catch (IOException e) { 32 | Log.error("Unable to load the inline JS events" + e.getMessage(), e); 33 | } 34 | } 35 | } 36 | 37 | public List findInlineJs(String source) { 38 | preloadEvents(); 39 | String[] tokens = source.split(" problematicLines = new ArrayList<>(); 42 | 43 | for(String line : tokens) { 44 | if(line.contains(" on")) { //Faster than regex 45 | for(String event : jsInlineEvents) { 46 | if(line.contains(" "+event)) { 47 | 48 | Pattern p = Pattern.compile(".{0,10} " + event + ".{0,50}"); 49 | Matcher m = p.matcher(line); 50 | 51 | if (m.find()) { 52 | problematicLines.add(m.group(0)); 53 | } 54 | } 55 | } 56 | } 57 | if(line.contains("]*).{0,50}"); 59 | Matcher m = p.matcher(line); 60 | if(m.find()) { 61 | String scriptTag = m.group(1); 62 | if(!scriptTag.contains("src=")) 63 | problematicLines.add(m.group(0)); 64 | } 65 | } 66 | } 67 | 68 | return problematicLines; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/util/PolicyBuilder.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.util; 2 | 3 | import ca.gosecure.cspauditor.model.Directive; 4 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 5 | import com.esotericsoftware.minlog.Log; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.NoSuchElementException; 13 | import java.util.StringTokenizer; 14 | 15 | public class PolicyBuilder { 16 | 17 | protected static List CSP_HEADERS = Arrays.asList("x-content-security-policy", "x-webkit-csp", "content-security-policy", "content-security-policy-report-only"); 18 | 19 | /** 20 | * Converted CSP header in the form of string to the object model. 21 | * @param headers Map of the CSP headers 22 | * @return 23 | */ 24 | public static List parseCspHeaders(Map headers) { 25 | List policies = new ArrayList<>(); 26 | 27 | for (Map.Entry header : headers.entrySet()) { 28 | ContentSecurityPolicy policy = new ContentSecurityPolicy(header.getKey()); 29 | String[] headerParts = header.getValue().split(";"); 30 | try { 31 | for (String part : headerParts) { 32 | List v = new ArrayList<>(); 33 | String trim = part.trim(); 34 | if(trim.equals("")) { 35 | continue; 36 | } 37 | StringTokenizer tokenizer = new StringTokenizer(trim, " "); 38 | 39 | String directiveName = tokenizer.nextToken(); 40 | while (tokenizer.hasMoreTokens()) { 41 | String value = tokenizer.nextToken().trim(); 42 | if (!value.equals("")) v.add(value); 43 | } 44 | Directive d = new Directive(directiveName, v); 45 | 46 | policy.addDirective(d); 47 | } 48 | 49 | } 50 | catch (NoSuchElementException e) { 51 | Log.error("Unexpected end of CSP directive: "+header.getValue()); 52 | } 53 | 54 | policies.add(policy); 55 | } 56 | return policies; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/java/ca/gosecure/cspauditor/util/SimpleListFile.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.function.Consumer; 8 | 9 | public class SimpleListFile { 10 | 11 | public static void openFile(String file, Consumer consumer) throws IOException { 12 | InputStream in = SimpleListFile.class.getResourceAsStream(file); 13 | 14 | BufferedReader r1 = new BufferedReader(new InputStreamReader(in)); 15 | String line; 16 | while((line = r1.readLine()) != null) { 17 | 18 | //Remove comment 19 | int indexComment = line.indexOf("#"); 20 | if (indexComment != -1) { 21 | line = line.substring(0, indexComment); 22 | } 23 | line = line.trim(); 24 | 25 | if ("".equals(line)) { 26 | continue; 27 | } 28 | consumer.accept(line); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/Media/scan_issue_decoration_info_certain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/csp-auditor-core/src/main/resources/resources/Media/scan_issue_decoration_info_certain.png -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/Media/scan_issue_high_certain_rpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/csp-auditor-core/src/main/resources/resources/Media/scan_issue_high_certain_rpt.png -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/Media/scan_issue_low_certain_rpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/csp-auditor-core/src/main/resources/resources/Media/scan_issue_low_certain_rpt.png -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/Media/scan_issue_medium_certain_rpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/csp-auditor-core/src/main/resources/resources/Media/scan_issue_medium_certain_rpt.png -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/data/csp_host_user_content.txt: -------------------------------------------------------------------------------- 1 | 2 | *.github.io 3 | github.com 4 | raw.githubusercontent.com 5 | 6 | #Amazon S3 7 | *.s3.amazonaws.com 8 | *.cloudfront.com 9 | 10 | #Heroku hosting 11 | *.herokuapp.com 12 | 13 | # 14 | dl.dropboxusercontent.com 15 | 16 | #AppEngine 17 | *.appspot.com 18 | 19 | #Google user files 20 | googleusercontent.com 21 | 22 | #Blogger 23 | *.blogspot.ae 24 | *.blogspot.al 25 | *.blogspot.am 26 | *.blogspot.ba 27 | *.blogspot.be 28 | *.blogspot.bg 29 | *.blogspot.bj 30 | *.blogspot.ca 31 | *.blogspot.cf 32 | *.blogspot.ch 33 | *.blogspot.cl 34 | *.blogspot.co.at 35 | *.blogspot.co.id 36 | *.blogspot.co.il 37 | *.blogspot.co.ke 38 | *.blogspot.co.nz 39 | *.blogspot.co.uk 40 | *.blogspot.co.za 41 | *.blogspot.com 42 | *.blogspot.com.ar 43 | *.blogspot.com.au 44 | *.blogspot.com.br 45 | *.blogspot.com.by 46 | *.blogspot.com.co 47 | *.blogspot.com.cy 48 | *.blogspot.com.ee 49 | *.blogspot.com.eg 50 | *.blogspot.com.es 51 | *.blogspot.com.mt 52 | *.blogspot.com.ng 53 | *.blogspot.com.tr 54 | *.blogspot.com.uy 55 | *.blogspot.cv 56 | *.blogspot.cz 57 | *.blogspot.de 58 | *.blogspot.dk 59 | *.blogspot.fi 60 | *.blogspot.fr 61 | *.blogspot.gr 62 | *.blogspot.hk 63 | *.blogspot.hr 64 | *.blogspot.hu 65 | *.blogspot.ie 66 | *.blogspot.in 67 | *.blogspot.is 68 | *.blogspot.it 69 | *.blogspot.jp 70 | *.blogspot.kr 71 | *.blogspot.li 72 | *.blogspot.lt 73 | *.blogspot.lu 74 | *.blogspot.md 75 | *.blogspot.mk 76 | *.blogspot.mr 77 | *.blogspot.mx 78 | *.blogspot.my 79 | *.blogspot.nl 80 | *.blogspot.no 81 | *.blogspot.pe 82 | *.blogspot.pt 83 | *.blogspot.qa 84 | *.blogspot.re 85 | *.blogspot.ro 86 | *.blogspot.rs 87 | *.blogspot.ru 88 | *.blogspot.se 89 | *.blogspot.sg 90 | *.blogspot.si 91 | *.blogspot.sk 92 | *.blogspot.sn 93 | *.blogspot.td 94 | *.blogspot.tw 95 | *.blogspot.ug 96 | *.blogspot.vn -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/data/csp_host_vulnerable_js.txt: -------------------------------------------------------------------------------- 1 | #https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js 2 | 3 | cdnjs.cloudflare.com/ajax/libs/angular.js/ 4 | 5 | #//code.angularjs.org/1.0.1/angular-1.0.1.js 6 | 7 | code.angularjs.org 8 | 9 | #//ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.js 10 | #//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js 11 | #//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js 12 | #//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e){alert(1337)}// 13 | #//ajax.googleapis.com/jsapi?callback=alert 14 | #//ajax.googleapis.com/ajax/services/feed/find?v=1.0&callback=click 15 | 16 | ajax.googleapis.com/ajax/libs/angularjs/ 17 | ajax.googleapis.com/ajax/libs/yui/ 18 | ajax.googleapis.com/jsapi 19 | ajax.googleapis.com/ajax/services/feed/find 20 | 21 | 22 | #http://d.yimg.com/autoc.finance.yahoo.com/autoc?callback=alert&query=yah&lang=en®ion=us 23 | d.yimg.com 24 | 25 | #JS Delivr 26 | cdn.jsdelivr.net 27 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/data/js_inline_event.txt: -------------------------------------------------------------------------------- 1 | # https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects 2 | 3 | onabort 4 | onauxclick 5 | oncancel 6 | oncanplay 7 | oncanplaythrough 8 | onchange 9 | onclick 10 | onclose 11 | oncontextmenu 12 | oncuechange 13 | ondblclick 14 | ondrag 15 | ondragend 16 | ondragenter 17 | ondragexit 18 | ondragleave 19 | ondragover 20 | ondragstart 21 | ondrop 22 | ondurationchange 23 | onemptied 24 | onended 25 | oninput 26 | oninvalid 27 | onkeydown 28 | onkeypress 29 | onkeyup 30 | onloadeddata 31 | onloadedmetadata 32 | onloadend 33 | onloadstart 34 | onmousedown 35 | onmouseenter 36 | onmouseleave 37 | onmousemove 38 | onmouseout 39 | onmouseover 40 | onmouseup 41 | onwheel 42 | onpause 43 | onplay 44 | onplaying 45 | onprogress 46 | onratechange 47 | onreset 48 | onsecuritypolicyviolation 49 | onseeked 50 | onseeking 51 | onselect 52 | onstalled 53 | onsubmit 54 | onsuspend 55 | ontimeupdate 56 | ontoggle 57 | onvolumechange 58 | onwaiting -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_deprecated_header_name.htm: -------------------------------------------------------------------------------- 1 |

The CSP configuration uses a deprecated header name.

2 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_risky_host_known_vulnerable_js.htm: -------------------------------------------------------------------------------- 1 |

The configuration could allow an attacker to load his own script.

2 | 3 |

The URL is considered to be unsafe because it is hosting a known vulnerable JavaScript library. 4 | By using this vulnerable script, an attacker could circumvent the CSP protection.

5 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_risky_host_user_content.htm: -------------------------------------------------------------------------------- 1 |

The configuration could allow an attacker to load his own script.

2 | 3 |

The URL is considered to be a Content Delivery Network (CDN) allowing other clients to upload their own files 4 | at this particular URL. By uploading a malicious script, an attacker could circumvent the CSP protection.

5 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_script_unsafe_eval.htm: -------------------------------------------------------------------------------- 1 |

The configuration removes the XSS sandboxing feature of Content Security Policy.

2 | 3 |

The 'unsafe-eval' directive allows scripts to evaluate strings as JavaScript. 4 | An attacker might be able to take advantage of those risky scripts using eval functions to trigger a DOM XSS. 5 | The directive is not a confirmation that eval() or setTimeout() are used.

6 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_script_unsafe_inline.htm: -------------------------------------------------------------------------------- 1 |

The configuration removes the XSS sandboxing feature of Content Security Policy.

2 | 3 |

The 'unsafe-inline' directive allows scripts to be inserted inline in the HTML page. 4 | An attacker will be able to use script html tag (<script>...</script>) or event handlers (onload, onerror, ...) to load malicious scripts.

5 | -------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_script_wildcard.htm: -------------------------------------------------------------------------------- 1 |

The configuration removes the XSS sandboxing feature of Content Security Policy.

2 | 3 |

The script-src directive allows scripts from any domain to be loaded. An attacker will be able to load external scripts if a XSS insertion point is present.

4 |
5 |

6 | References:
7 |
8 | CSP Policy Directives 9 |

-------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_style.htm: -------------------------------------------------------------------------------- 1 |

The configuration allows the inclusion of arbitrary stylesheets. Stylesheet inclusion has very limited possibilities.

2 |

3 | References:
4 |
5 | About CSS Attacks by Eduardo Vela
6 | CSS based Attack: Abusing unicode-range of @font-face by Masato Kinugawa
7 | Scriptless Attacks - Stealing the Pie Without Touching the Sill by Mario Heiderich, Marcus Niemietz, Felix Schuster, Thorsten Holz, Jörg Schwenk
8 | Detecting and exploiting path-relative stylesheet import (PRSSI) vulnerabilities by James Kettle 9 |

-------------------------------------------------------------------------------- /csp-auditor-core/src/main/resources/resources/descriptions/issue_wildcard_limited.htm: -------------------------------------------------------------------------------- 1 |

The configuration allows arbitrary resources to be loaded. Although, it is not known to open the website to script insertion.

2 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/BaseCspTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class BaseCspTest { 7 | 8 | 9 | protected static Map wrapInMap(String value) { 10 | Map map = new HashMap<>(); 11 | map.put("content-security-policy",value); 12 | return map; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/gui/CspGeneratorPanelTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui; 2 | 3 | import ca.gosecure.cspauditor.gui.generator.CspGeneratorPanel; 4 | import ca.gosecure.cspauditor.gui.generator.CspGeneratorPanelController; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.util.Arrays; 9 | 10 | public class CspGeneratorPanelTest { 11 | public static void main(String[] args) { 12 | 13 | JFrame frame = new JFrame("Testing frame"); 14 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15 | 16 | 17 | CspGeneratorPanel panel = new CspGeneratorPanel(new CspGeneratorPanelController() { 18 | @Override 19 | public void analyzeDomain(String domain) { 20 | 21 | } 22 | 23 | @Override 24 | public void refreshDomains() { 25 | 26 | } 27 | 28 | @Override 29 | public void selectResource(String path) { 30 | 31 | } 32 | 33 | @Override 34 | public void selectInline(String url) { 35 | 36 | } 37 | 38 | @Override 39 | public void selectReport(String id) { 40 | 41 | } 42 | }); 43 | panel.init(); 44 | 45 | panel.addDomains(Arrays.asList("facebook.com","shopify.com","yahoo.ca")); 46 | 47 | panel.setConfiguration(new JTextArea("default-src 'self'; script-src 'self' www.google-analytics.com ssl.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' www.google-analytics.com ssl.google-analytics.com; object-src 'none'; media-src 'none'; frame-src 'none'")); 48 | 49 | panel.addResource("00","AAAAAAAAAAAAAAA","SHOULD NOT BE VISIBLE"); 50 | panel.clearResources(); 51 | panel.addResource("1","","/conferences/?test=1b&test=1"); 52 | panel.addResource("2","","/index?test=1b&test=1"); 53 | 54 | panel.setResourceItem(new JTextArea("")); 55 | 56 | 57 | panel.setInlineItem(new JTextArea("Inline")); 58 | 59 | panel.setReportItem(new JTextArea("Report")); 60 | 61 | frame.add(panel.getRootPanel()); 62 | frame.pack(); 63 | frame.setMinimumSize(new Dimension(600,400)); 64 | frame.setLocationRelativeTo(null); 65 | frame.setVisible(true); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/gui/CspHeadersPanelTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.gui; 2 | 3 | import ca.gosecure.cspauditor.BaseCspTest; 4 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 5 | import ca.gosecure.cspauditor.util.PolicyBuilder; 6 | 7 | import javax.swing.*; 8 | import java.awt.*; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class CspHeadersPanelTest extends BaseCspTest { 14 | 15 | 16 | public static void main(String[] args) { 17 | // List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src * data: blob:;script-src *.facebook.com *.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* 'unsafe-inline' 'unsafe-eval' fbstatic-a.akamaihd.net fbcdn-static-b-a.akamaihd.net *.atlassolutions.com blob: chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl;style-src * 'unsafe-inline' data:;connect-src *.facebook.com *.fbcdn.net *.facebook.net *.spotilocal.com:* *.akamaihd.net wss://*.facebook.com:* https://fb.scanandcleanlocal.com:* *.atlassolutions.com attachment.fbsbx.com blob: 127.0.0.1:* http://*.serialpodcast.org https://*.serialpodcast.org;")); 18 | // List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'self' https:; connect-src 'self' https: wss:; font-src 'self' https: data:; frame-src 'self' https: data:; img-src 'self' https: data:; media-src 'self' https: data:; object-src 'self' https:; script-src 'self' https: 'unsafe-inline' 'unsafe-eval'; style-src 'self' https: 'unsafe-inline'; report-uri /csp-report/2eeecf42-d1f1-4403-89fa-06d1b8a093c0?source%5Baction%5D=index&source%5Bcontroller%5D=home&source%5Bdomain%5D=www.shopify.ca&source%5Bsection%5D=brochure;")); 19 | // List p = PolicyBuilder.parseCspHeaders(wrapInMap("script-src https://clients4.google.com/insights/consumersurveys/ 'self' 'unsafe-inline' 'unsafe-eval' https://hangouts.google.com/ https://talkgadget.google.com/ https://*.talkgadget.google.com/ https://www.googleapis.com/appsmarket/v2/installedApps/ https://www-gm-opensocial.googleusercontent.com/gadgets/js/ https://docs.google.com/static/doclist/client/js/ https://www.google.com/tools/feedback/ https://s.ytimg.com/yts/jsbin/ https://www.youtube.com/iframe_api https://ssl.google-analytics.com/ https://apis.google.com/_/scs/abc-static/ https://apis.google.com/js/ https://clients1.google.com/complete/ https://apis.google.com/_/scs/apps-static/_/js/ https://ssl.gstatic.com/inputtools/js/ https://ssl.gstatic.com/cloudsearch/static/o/js/ https://www.gstatic.com/feedback/js/ https://www.gstatic.com/common_sharing/static/client/js/ https://www.gstatic.com/og/_/js/;frame-src https://clients4.google.com/insights/consumersurveys/ https://calendar.google.com/accounts/ 'self' https://accounts.google.com/ https://apis.google.com/u/ https://apis.google.com/_/streamwidgets/ https://clients6.google.com/static/ https://content.googleapis.com/static/ https://mail-attachment.googleusercontent.com/ https://www.google.com/calendar/ https://calendar.google.com/calendar/ https://docs.google.com/ https://drive.google.com https://*.googleusercontent.com/docs/securesc/ https://feedback.googleusercontent.com/resources/ https://www.google.com/tools/feedback/ https://support.google.com/inapp/ https://*.googleusercontent.com/gadgets/ifr https://hangouts.google.com/ https://talkgadget.google.com/ https://*.talkgadget.google.com/ https://www-gm-opensocial.googleusercontent.com/gadgets/ https://plus.google.com/ https://wallet.google.com/gmail/ https://www.youtube.com/embed/ https://clients5.google.com/pagead/drt/dn/ https://clients5.google.com/ads/measurement/jn/ https://www.gstatic.com/mail/ww/ https://www.gstatic.com/mail/intl/ https://clients5.google.com/webstore/wall/ https://ci3.googleusercontent.com/ https://apis.google.com/additnow/ https://www.gstatic.com/mail/promo/;report-uri /mail/cspreport;object-src https://mail-attachment.googleusercontent.com/swfs/ https://mail-attachment.googleusercontent.com/attachment/")); 20 | // List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'self' https://*.paypal.com https://*.paypalobjects.com https://nexus.ensighten.com 'unsafe-inline'; frame-src 'self' https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/embed/ https://www.paypal-donations.com https://www.paypal-donations.co.uk https://*.qa.missionfish.org https://www.youtube-nocookie.com; script-src 'self' https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/iframe_api https://s.ytimg.com/yts/jsbin/ https://*.t.eloqua.com https://img.en25.com/i/elqCfg.min.js https://query.yahooapis.com/ 'unsafe-inline' 'unsafe-eval'; connect-src 'self' https://*.paypal.com https://*.paypalobjects.com https://*.salesforce.com https://*.force.com https://*.eloqua.com https://nexus.ensighten.com https://storelocator.api.where.com https://api.paypal-retaillocator.com https://nominatim.openstreetmap.org https://www.paypal-biz.com; img-src 'self' * data:; object-src 'self' https://*.paypal.com https://*.paypalobjects.com; font-src 'self' https://*.paypalobjects.com;")); 21 | // List p = PolicyBuilder.parseCspHeaders(wrapInMap("frame-ancestors 'self' *.spotify.com https://*.spotify.net https://*.optimizely.com https://*.optimizelyedit.com")); 22 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("img-src https://* data: blob: ; connect-src https://* ws://127.0.0.1:*/ws ; media-src https://* ; object-src https://cf.dropboxstatic.com/static/ https://www.dropboxstatic.com/static/ 'self' https://flash.dropboxstatic.com https://swf.dropboxstatic.com https://dbxlocal.dropboxstatic.com ; default-src 'none' ; font-src https://* data: ; frame-src https://* carousel://* dbapi-6://* itms-apps://* itms-appss://* ; style-src https://* 'unsafe-inline' 'unsafe-eval' ; script-src https://ajax.googleapis.com/ajax/libs/jquery/ 'unsafe-eval' 'self' https://cf.dropboxstatic.com/static/javascript/ https://www.dropboxstatic.com/static/javascript/ https://cf.dropboxstatic.com/static/api/ https://www.dropboxstatic.com/static/api/ https://www.google.com/recaptcha/api/ 'nonce-yDqEXWDgP2zUUhD8Po0j' ;")); 23 | 24 | displayTestPolicy(p); 25 | } 26 | 27 | public static void displayTestPolicy(List p) { 28 | 29 | JFrame frame = new JFrame("Testing frame"); 30 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 31 | 32 | CspHeadersPanel headers = new CspHeadersPanel(); 33 | headers.displayPolicy(p); 34 | 35 | frame.add(headers.getComponent()); 36 | frame.pack(); 37 | frame.setLocationRelativeTo(null); 38 | frame.setVisible(true); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/model/HeaderValidationTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import ca.gosecure.cspauditor.BaseCspTest; 4 | import ca.gosecure.cspauditor.util.PolicyBuilder; 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.List; 8 | 9 | import static org.testng.Assert.fail; 10 | 11 | public class HeaderValidationTest extends BaseCspTest { 12 | 13 | private static String CSP_ALLOW_ALL = "default-src: *"; 14 | 15 | @Test 16 | public void findUnsafeEval() { 17 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("script-src 'unsafe-eval'")); 18 | List issues = HeaderValidation.validateCspConfig(p); 19 | System.out.println(p); 20 | hasIssueType(issues, "issue_script_unsafe_eval"); 21 | 22 | 23 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'unsafe-inline'; script-src 'unsafe-eval'")); 24 | issues = HeaderValidation.validateCspConfig(p); 25 | System.out.println(p); 26 | hasIssueType(issues, "issue_script_unsafe_eval"); 27 | 28 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src *; script-src 'unsafe-eval'")); 29 | issues = HeaderValidation.validateCspConfig(p); 30 | System.out.println(p); 31 | hasIssueType(issues, "issue_script_unsafe_eval"); 32 | 33 | 34 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self; script-src 'unsafe-eval'")); 35 | issues = HeaderValidation.validateCspConfig(p); 36 | System.out.println(p); 37 | hasIssueType(issues, "issue_script_unsafe_eval"); 38 | } 39 | 40 | @Test 41 | public void findUnsafeInline() { 42 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("script-src 'unsafe-inline'")); 43 | List issues = HeaderValidation.validateCspConfig(p); 44 | System.out.println(p); 45 | hasIssueType(issues, "issue_script_unsafe_inline"); 46 | 47 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'unsafe-inline'")); 48 | issues = HeaderValidation.validateCspConfig(p); 49 | System.out.println(p); 50 | hasIssueType(issues, "issue_script_unsafe_inline"); 51 | 52 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self, xss.lol ; script-src 'unsafe-inline'")); 53 | issues = HeaderValidation.validateCspConfig(p); 54 | System.out.println(p); 55 | hasIssueType(issues, "issue_script_unsafe_inline"); 56 | } 57 | 58 | @Test 59 | public void findScriptWildCard() { 60 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("")); 61 | List issues = HeaderValidation.validateCspConfig(p); 62 | System.out.println(p); 63 | hasIssueType(issues, "issue_script_wildcard"); 64 | 65 | 66 | p = PolicyBuilder.parseCspHeaders(wrapInMap("script-src *")); 67 | issues = HeaderValidation.validateCspConfig(p); 68 | System.out.println(p); 69 | hasIssueType(issues, "issue_script_wildcard"); 70 | 71 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self; script-src *")); 72 | issues = HeaderValidation.validateCspConfig(p); 73 | System.out.println(p); 74 | hasIssueType(issues, "issue_script_wildcard"); 75 | 76 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self, xss.lol ; script-src *")); 77 | issues = HeaderValidation.validateCspConfig(p); 78 | System.out.println(p); 79 | hasIssueType(issues, "issue_script_wildcard"); 80 | } 81 | 82 | @Test 83 | public void findUnsafeStyle() { 84 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("")); 85 | List issues = HeaderValidation.validateCspConfig(p); 86 | System.out.println(p); 87 | hasIssueType(issues, "issue_style"); 88 | 89 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self; style-src *")); 90 | issues = HeaderValidation.validateCspConfig(p); 91 | System.out.println(p); 92 | hasIssueType(issues, "issue_style"); 93 | 94 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src *")); 95 | issues = HeaderValidation.validateCspConfig(p); 96 | System.out.println(p); 97 | hasIssueType(issues, "issue_style"); 98 | } 99 | 100 | @Test 101 | public void findWildcardLowRisk() { 102 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("")); 103 | List issues = HeaderValidation.validateCspConfig(p); 104 | System.out.println(p); 105 | hasIssueType(issues, "issue_wildcard_limited"); 106 | 107 | p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src self; media-src *")); 108 | issues = HeaderValidation.validateCspConfig(p); 109 | System.out.println(p); 110 | hasIssueType(issues, "issue_wildcard_limited"); 111 | } 112 | 113 | 114 | private void hasIssueType(List issues, String msg) { 115 | System.out.println(issues.size() + " found"); 116 | boolean issueFound = false; 117 | for(CspIssue issue: issues) { 118 | System.out.println(" - "+issue); 119 | if(issue.getMessage().equals(msg)) { 120 | issueFound = true; 121 | } 122 | } 123 | if(!issueFound) fail("Unable to find the issue of the type "+msg); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/model/WeakCdnHostTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.model; 2 | 3 | import com.esotericsoftware.minlog.Log; 4 | import org.testng.annotations.Test; 5 | 6 | import static org.testng.Assert.assertTrue; 7 | 8 | public class WeakCdnHostTest { 9 | 10 | @Test 11 | public void loadingBlacklistUserContentHosts() { 12 | Log.DEBUG(); 13 | 14 | WeakCdnHost wch = WeakCdnHost.getInstance(); 15 | 16 | System.out.println(wch.getBlacklistUserContentHosts().size() + " hosts allowing user content to be uploaded."); 17 | 18 | for(String host : wch.getBlacklistUserContentHosts()) { 19 | System.out.println(host); 20 | } 21 | } 22 | 23 | @Test 24 | public void loadingBlacklistVulnerableJsHosts() { 25 | Log.DEBUG(); 26 | 27 | WeakCdnHost wch = WeakCdnHost.getInstance(); 28 | 29 | System.out.println(wch.getBlacklistVulnerableJsHosts().size() + " hosts with vulnerable JavaScript"); 30 | 31 | for(String host : wch.getBlacklistVulnerableJsHosts()) { 32 | System.out.println(host); 33 | } 34 | } 35 | 36 | @Test 37 | public void findGoogleApis() { 38 | WeakCdnHost wch = WeakCdnHost.getInstance(); 39 | 40 | //Testing a couple of variations 41 | assertTrue(wch.isHostingVulnerableJs("*.googleapis.com")); 42 | assertTrue(wch.isHostingVulnerableJs("ajax.googleapis.com")); 43 | assertTrue(wch.isHostingVulnerableJs("ajax.googleapis.com/ajax/libs/")); 44 | assertTrue(wch.isHostingVulnerableJs("ajax.googleapis.com/ajax/")); 45 | assertTrue(wch.isHostingVulnerableJs("ajax.googleapis.com/")); 46 | assertTrue(wch.isHostingVulnerableJs("https://ajax.googleapis.com/")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /csp-auditor-core/src/test/java/ca/gosecure/cspauditor/util/ContentSecurityPolicyBuilderTest.java: -------------------------------------------------------------------------------- 1 | package ca.gosecure.cspauditor.util; 2 | 3 | import ca.gosecure.cspauditor.BaseCspTest; 4 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class ContentSecurityPolicyBuilderTest extends BaseCspTest { 12 | 13 | 14 | @Test 15 | public void loadFacebookCsp() { 16 | System.out.println("Facebook"); 17 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src * data: blob:;script-src *.facebook.com *.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* 'unsafe-inline' 'unsafe-eval' fbstatic-a.akamaihd.net fbcdn-static-b-a.akamaihd.net *.atlassolutions.com blob: chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl;style-src * 'unsafe-inline' data:;connect-src *.facebook.com *.fbcdn.net *.facebook.net *.spotilocal.com:* *.akamaihd.net wss://*.facebook.com:* https://fb.scanandcleanlocal.com:* *.atlassolutions.com attachment.fbsbx.com blob: 127.0.0.1:* http://*.serialpodcast.org https://*.serialpodcast.org;")); 18 | System.out.println(p); 19 | } 20 | @Test 21 | public void loadGmailCsp(){ 22 | System.out.println("GMail"); 23 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("script-src https://clients4.google.com/insights/consumersurveys/ 'self' 'unsafe-inline' 'unsafe-eval' https://hangouts.google.com/ https://talkgadget.google.com/ https://*.talkgadget.google.com/ https://www.googleapis.com/appsmarket/v2/installedApps/ https://www-gm-opensocial.googleusercontent.com/gadgets/js/ https://docs.google.com/static/doclist/client/js/ https://www.google.com/tools/feedback/ https://s.ytimg.com/yts/jsbin/ https://www.youtube.com/iframe_api https://ssl.google-analytics.com/ https://apis.google.com/_/scs/abc-static/ https://apis.google.com/js/ https://clients1.google.com/complete/ https://apis.google.com/_/scs/apps-static/_/js/ https://ssl.gstatic.com/inputtools/js/ https://ssl.gstatic.com/cloudsearch/static/o/js/ https://www.gstatic.com/feedback/js/ https://www.gstatic.com/common_sharing/static/client/js/ https://www.gstatic.com/og/_/js/;frame-src https://clients4.google.com/insights/consumersurveys/ https://calendar.google.com/accounts/ 'self' https://accounts.google.com/ https://apis.google.com/u/ https://apis.google.com/_/streamwidgets/ https://clients6.google.com/static/ https://content.googleapis.com/static/ https://mail-attachment.googleusercontent.com/ https://www.google.com/calendar/ https://calendar.google.com/calendar/ https://docs.google.com/ https://drive.google.com https://*.googleusercontent.com/docs/securesc/ https://feedback.googleusercontent.com/resources/ https://www.google.com/tools/feedback/ https://support.google.com/inapp/ https://*.googleusercontent.com/gadgets/ifr https://hangouts.google.com/ https://talkgadget.google.com/ https://*.talkgadget.google.com/ https://www-gm-opensocial.googleusercontent.com/gadgets/ https://plus.google.com/ https://wallet.google.com/gmail/ https://www.youtube.com/embed/ https://clients5.google.com/pagead/drt/dn/ https://clients5.google.com/ads/measurement/jn/ https://www.gstatic.com/mail/ww/ https://www.gstatic.com/mail/intl/ https://clients5.google.com/webstore/wall/ https://ci3.googleusercontent.com/ https://apis.google.com/additnow/ https://www.gstatic.com/mail/promo/;report-uri /mail/cspreport;object-src https://mail-attachment.googleusercontent.com/swfs/ https://mail-attachment.googleusercontent.com/attachment/")); 24 | System.out.println(p); 25 | } 26 | 27 | @Test 28 | public void loadShopifyCsp() { 29 | System.out.println("Shopify"); 30 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'self' https:; connect-src 'self' https: wss:; font-src 'self' https: data:; frame-src 'self' https: data:; img-src 'self' https: data:; media-src 'self' https: data:; object-src 'self' https:; script-src 'self' https: 'unsafe-inline' 'unsafe-eval'; style-src 'self' https: 'unsafe-inline'; report-uri /csp-report/2eeecf42-d1f1-4403-89fa-06d1b8a093c0?source%5Baction%5D=index&source%5Bcontroller%5D=home&source%5Bdomain%5D=www.shopify.ca&source%5Bsection%5D=brochure;")); 31 | System.out.println(p); 32 | } 33 | 34 | @Test 35 | public void loadPaypalCsp() { 36 | System.out.println("Paypal"); 37 | List p = PolicyBuilder.parseCspHeaders(wrapInMap("default-src 'self' https://*.paypal.com https://*.paypalobjects.com https://nexus.ensighten.com 'unsafe-inline'; frame-src 'self' https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/embed/ https://www.paypal-donations.com https://www.paypal-donations.co.uk https://*.qa.missionfish.org https://www.youtube-nocookie.com; script-src 'self' https://*.paypal.com https://*.paypalobjects.com https://www.youtube.com/iframe_api https://s.ytimg.com/yts/jsbin/ https://*.t.eloqua.com https://img.en25.com/i/elqCfg.min.js https://query.yahooapis.com/ 'unsafe-inline' 'unsafe-eval'; connect-src 'self' https://*.paypal.com https://*.paypalobjects.com https://*.salesforce.com https://*.force.com https://*.eloqua.com https://nexus.ensighten.com https://storelocator.api.where.com https://api.paypal-retaillocator.com https://nominatim.openstreetmap.org https://www.paypal-biz.com; img-src 'self' * data:; object-src 'self' https://*.paypal.com https://*.paypalobjects.com; font-src 'self' https://*.paypalobjects.com;")); 38 | System.out.println(p); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | group 'csp-auditor' 2 | version '2.0-SNAPSHOT' 3 | 4 | repositories { 5 | flatDir { 6 | dirs 'libs' 7 | } 8 | } 9 | 10 | dependencies { 11 | compile group: 'com.esotericsoftware', name: 'minlog', version: '1.3' 12 | testCompile group: 'org.testng', name: 'testng', version: '6.8.8' 13 | compile project(':csp-auditor-core') 14 | //ZAP Dependencies 15 | compile group: 'commons-configuration', name: 'commons-configuration', version: '1.10' 16 | compile group: 'net.htmlparser.jericho', name: 'jericho-html', version: '3.1' 17 | compile group: 'log4j', name: 'log4j', version: '1.2.17' 18 | compile group: 'commons-httpclient', name: 'commons-httpclient', version: '3.1' 19 | compile name: 'zap-2.5.0' //Static jar 20 | } 21 | 22 | 23 | 24 | jar{ 25 | archiveName 'cspauditor-alpha-2.zap' 26 | 27 | from { 28 | configurations.compile.findAll{ 29 | //Exclude ZAP provided dependency (see above) 30 | !(it.getName() in ['zap-2.5.0.jar','jericho-html-3.1.jar','commons-configuration-1.10.jar', 31 | 'commons-httpclient-3.1.jar', 'log4j-1.2.17.jar'] || 32 | it.getName().startsWith("commons-")) 33 | }.collect { 34 | //println it.getName(); 35 | return it.isDirectory() ? it : zipTree(it) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/libs/zap-2.5.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/csp-auditor-zap-plugin/libs/zap-2.5.0.jar -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/CspAuditorExtension.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import org.parosproxy.paros.extension.ExtensionAdaptor; 4 | import org.parosproxy.paros.extension.ExtensionHook; 5 | import org.zaproxy.zap.extension.httppanel.component.split.response.ResponseSplitComponent; 6 | import org.zaproxy.zap.view.HttpPanelManager; 7 | 8 | public class CspAuditorExtension extends ExtensionAdaptor { 9 | 10 | @Override 11 | public String getAuthor() { 12 | return "Philippe Arteau"; 13 | } 14 | 15 | @Override 16 | public void hook(ExtensionHook extensionHook) { 17 | 18 | if (getView() != null) { 19 | HttpPanelManager panelManager = HttpPanelManager.getInstance(); 20 | panelManager.addResponseViewFactory(ResponseSplitComponent.NAME, new ResponseCspViewFactory()); 21 | panelManager.addResponseDefaultViewSelectorFactory(ResponseSplitComponent.NAME, new ResponseImageMetadataViewSelectorFactory()); 22 | } 23 | } 24 | 25 | @Override 26 | public void unload() { 27 | if (getView() != null) { 28 | HttpPanelManager panelManager = HttpPanelManager.getInstance(); 29 | panelManager.removeResponseViewFactory(ResponseSplitComponent.NAME, 30 | ResponseCspViewFactory.NAME); 31 | panelManager.removeResponseViews( 32 | ResponseSplitComponent.NAME, 33 | ResponseCspView.NAME, 34 | ResponseSplitComponent.ViewComponent.BODY); 35 | 36 | panelManager.removeResponseDefaultViewSelectorFactory( 37 | ResponseSplitComponent.NAME, 38 | ResponseCspViewFactory.NAME); 39 | panelManager.removeResponseDefaultViewSelectors(ResponseSplitComponent.NAME, 40 | ResponseCspViewSelector.NAME, 41 | ResponseSplitComponent.ViewComponent.BODY); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/CspAuditorPassiveScanner.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 4 | import ca.gosecure.cspauditor.model.CspIssue; 5 | import ca.gosecure.cspauditor.model.HeaderValidation; 6 | import com.esotericsoftware.minlog.Log; 7 | import org.parosproxy.paros.core.scanner.Alert; 8 | import org.parosproxy.paros.network.HttpMessage; 9 | import org.zaproxy.zap.extension.pscan.PassiveScanThread; 10 | import org.zaproxy.zap.extension.pscan.PluginPassiveScanner; 11 | import net.htmlparser.jericho.Source; 12 | import org.apache.log4j.Logger; 13 | 14 | import java.util.List; 15 | 16 | public class CspAuditorPassiveScanner extends PluginPassiveScanner { 17 | 18 | private static int PLUGIN_ID = 0x99991111; 19 | 20 | private static Logger logger = Logger.getLogger(CspAuditorPassiveScanner.class); 21 | 22 | 23 | private PassiveScanThread parent = null; 24 | 25 | 26 | public CspAuditorPassiveScanner() { 27 | Log.setLogger(new Log.Logger() { 28 | @Override 29 | public void log(int level, String category, String message, Throwable ex) { 30 | if(ex != null) { 31 | logger.error(message,ex); 32 | } 33 | else { 34 | logger.info(message); 35 | } 36 | } 37 | }); 38 | Log.DEBUG(); 39 | } 40 | 41 | @Override 42 | public void scanHttpRequestSend(HttpMessage httpMessage, int i) { 43 | 44 | } 45 | 46 | @Override 47 | public void scanHttpResponseReceive(HttpMessage httpMessage, int refId, Source source) { 48 | List csp = ZapPolicyBuilder.buildFromResponse(httpMessage); 49 | 50 | List cspIssues = HeaderValidation.validateCspConfig(csp); 51 | 52 | convertIssues(cspIssues,httpMessage,refId); 53 | } 54 | 55 | private void convertIssues(List cspIssues,HttpMessage message, int refId) { 56 | for(CspIssue issue : cspIssues) { 57 | Alert alert = new Alert(PLUGIN_ID, mapToZapSeverity(issue.getSeverity()), Alert.CONFIDENCE_HIGH, "CSP: "+issue.getTitle()); 58 | alert.setDetail(stripHtmlTag(issue.getLocalizedMessage()), 59 | message.getRequestHeader().getURI().toString(), 60 | issue.getDirective() != null ? issue.getDirective().getName() : issue.getHighlightedValue(), //Param : CSP Directive or Header 61 | "", //Attack 62 | "", //Other info 63 | "", //Solution 64 | "", //One hyperlink 65 | message 66 | ); 67 | this.parent.raiseAlert(refId,alert); 68 | } 69 | 70 | } 71 | 72 | private String stripHtmlTag(String htmlMessage) { 73 | return htmlMessage.replaceAll("<[/]?b>","*") 74 | .replaceAll("","[") 75 | .replaceAll("","]") 76 | .replaceAll("<[/]?[a-z]+[/]?>","") 77 | .replaceAll("",": ") 78 | .replaceAll("<","<").replaceAll(">",">"); 79 | } 80 | 81 | 82 | private int mapToZapSeverity(int severity) { 83 | if(severity == CspIssue.HIGH) { 84 | return Alert.RISK_HIGH; 85 | } 86 | else if (severity == CspIssue.MED) { 87 | return Alert.RISK_MEDIUM; 88 | } 89 | else if (severity == CspIssue.LOW) { 90 | return Alert.RISK_LOW; 91 | } 92 | return Alert.RISK_LOW; 93 | } 94 | 95 | @Override 96 | public void setParent(PassiveScanThread thread) { 97 | this.parent = thread; 98 | } 99 | 100 | @Override 101 | public String getName() { 102 | return "CSP Auditor"; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/ResponseCspView.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import ca.gosecure.cspauditor.gui.CspHeadersPanel; 4 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 5 | import org.apache.commons.configuration.FileConfiguration; 6 | import org.parosproxy.paros.network.HttpMessage; 7 | import org.zaproxy.zap.extension.httppanel.Message; 8 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelView; 9 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelViewModel; 10 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelViewModelEvent; 11 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelViewModelListener; 12 | 13 | import javax.swing.*; 14 | import java.util.ArrayList; 15 | import java.util.Map; 16 | 17 | public class ResponseCspView implements HttpPanelView, HttpPanelViewModelListener { 18 | 19 | public static final String NAME = ResponseCspView.class.getName(); 20 | private HttpPanelViewModel model; 21 | 22 | private CspHeadersPanel propertyPanel; 23 | 24 | public ResponseCspView(HttpPanelViewModel model) { 25 | this.model = model; 26 | 27 | propertyPanel = new CspHeadersPanel(); 28 | 29 | this.model.addHttpPanelViewModelListener(this); 30 | } 31 | 32 | @Override 33 | public String getName() { 34 | return NAME; 35 | } 36 | 37 | @Override 38 | public String getCaptionName() { 39 | return "CSP"; 40 | } 41 | 42 | @Override 43 | public String getTargetViewName() { 44 | return ""; 45 | } 46 | 47 | @Override 48 | public int getPosition() { 49 | return 3; 50 | } 51 | 52 | @Override 53 | public JComponent getPane() { 54 | return propertyPanel.getComponent(); 55 | } 56 | 57 | @Override 58 | public void setSelected(boolean b) { 59 | 60 | } 61 | 62 | @Override 63 | public void save() { 64 | } 65 | 66 | @Override 67 | public HttpPanelViewModel getModel() { 68 | return model; 69 | } 70 | 71 | @Override 72 | public boolean isEnabled(Message message) { 73 | return hasCspHeader(message); 74 | } 75 | 76 | @Override 77 | public boolean hasChanged() { 78 | return false; 79 | } 80 | 81 | @Override 82 | public boolean isEditable() { 83 | return false; 84 | } 85 | 86 | @Override 87 | public void setEditable(boolean b) { 88 | } 89 | 90 | @Override 91 | public void setParentConfigurationKey(String s) { 92 | } 93 | 94 | @Override 95 | public void loadConfiguration(FileConfiguration fileConfiguration) { 96 | 97 | } 98 | 99 | @Override 100 | public void saveConfiguration(FileConfiguration fileConfiguration) { 101 | 102 | } 103 | 104 | static boolean hasCspHeader(final Message aMessage) { 105 | if(!(aMessage instanceof HttpMessage)) { 106 | return false; 107 | } 108 | HttpMessage httpMessage = (HttpMessage) aMessage; 109 | 110 | Map headers = ZapPolicyBuilder.getCspHeader(httpMessage); 111 | return headers.size() > 0; 112 | } 113 | 114 | @Override 115 | public void dataChanged(HttpPanelViewModelEvent event) { 116 | HttpMessage httpMessage = (HttpMessage) model.getMessage(); 117 | 118 | if (hasCspHeader(httpMessage)) { 119 | propertyPanel.displayPolicy(ZapPolicyBuilder.buildFromResponse(httpMessage)); 120 | } else { 121 | propertyPanel.displayPolicy(new ArrayList()); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/ResponseCspViewFactory.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import org.zaproxy.zap.extension.httppanel.component.split.response.ResponseSplitComponent; 4 | import org.zaproxy.zap.extension.httppanel.view.DefaultHttpPanelViewModel; 5 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelView; 6 | import org.zaproxy.zap.view.HttpPanelManager; 7 | 8 | public class ResponseCspViewFactory implements HttpPanelManager.HttpPanelViewFactory { 9 | 10 | public static final String NAME = ResponseCspViewFactory.class.getName(); 11 | 12 | @Override 13 | public String getName() { 14 | return NAME; 15 | } 16 | 17 | @Override 18 | public HttpPanelView getNewView() { 19 | return new ResponseCspView(new DefaultHttpPanelViewModel()); 20 | } 21 | 22 | @Override 23 | public Object getOptions() { 24 | return ResponseSplitComponent.ViewComponent.BODY; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/ResponseCspViewSelector.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import org.zaproxy.zap.extension.httppanel.Message; 4 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelDefaultViewSelector; 5 | 6 | public class ResponseCspViewSelector implements HttpPanelDefaultViewSelector { 7 | 8 | public static final String NAME = ResponseCspViewSelector.class.getName(); 9 | 10 | @Override 11 | public String getName() { 12 | return NAME; 13 | } 14 | 15 | @Override 16 | public boolean matchToDefaultView(Message aMessage) { 17 | return ResponseCspView.hasCspHeader(aMessage); 18 | } 19 | 20 | @Override 21 | public String getViewName() { 22 | return ResponseCspView.NAME; 23 | } 24 | 25 | @Override 26 | public int getOrder() { 27 | return 30; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/ResponseImageMetadataViewSelectorFactory.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import org.zaproxy.zap.extension.httppanel.component.split.response.ResponseSplitComponent; 4 | import org.zaproxy.zap.extension.httppanel.view.HttpPanelDefaultViewSelector; 5 | import org.zaproxy.zap.view.HttpPanelManager; 6 | 7 | public class ResponseImageMetadataViewSelectorFactory implements HttpPanelManager.HttpPanelDefaultViewSelectorFactory { 8 | 9 | public static final String NAME = ResponseImageMetadataViewSelectorFactory.class.getName(); 10 | 11 | private static HttpPanelDefaultViewSelector defaultViewSelector = null; 12 | 13 | private HttpPanelDefaultViewSelector getDefaultViewSelector() { 14 | if (defaultViewSelector == null) { 15 | createViewSelector(); 16 | } 17 | return defaultViewSelector; 18 | } 19 | 20 | private synchronized void createViewSelector() { 21 | if (defaultViewSelector == null) { 22 | defaultViewSelector = new ResponseCspViewSelector(); 23 | } 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return NAME; 29 | } 30 | 31 | @Override 32 | public HttpPanelDefaultViewSelector getNewDefaultViewSelector() { 33 | return getDefaultViewSelector(); 34 | } 35 | 36 | @Override 37 | public Object getOptions() { 38 | return ResponseSplitComponent.ViewComponent.BODY; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/java/org/zaproxy/zap/extension/cspauditor/ZapPolicyBuilder.java: -------------------------------------------------------------------------------- 1 | package org.zaproxy.zap.extension.cspauditor; 2 | 3 | import ca.gosecure.cspauditor.model.ContentSecurityPolicy; 4 | import ca.gosecure.cspauditor.util.PolicyBuilder; 5 | import org.parosproxy.paros.network.HttpHeaderField; 6 | import org.parosproxy.paros.network.HttpMessage; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class ZapPolicyBuilder extends PolicyBuilder { 13 | 14 | public static Map getCspHeader(HttpMessage httpMessage) { 15 | Map headers = new HashMap<>(); 16 | 17 | for(HttpHeaderField header : httpMessage.getResponseHeader().getHeaders()) { 18 | String headerLower = header.getName().toLowerCase(); 19 | 20 | for(String cspHeader : CSP_HEADERS) { 21 | if (headerLower.equals(cspHeader)) { 22 | headers.put(header.getName(), header.getValue()); 23 | } 24 | } 25 | } 26 | 27 | return headers; 28 | } 29 | 30 | public static List buildFromResponse(HttpMessage httpMessage) { 31 | Map headers = getCspHeader(httpMessage); 32 | return parseCspHeaders(headers); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /csp-auditor-zap-plugin/src/main/resources/ZapAddOn.xml: -------------------------------------------------------------------------------- 1 | 2 | CSP Auditor 3 | 1 4 | Display Content Security Policy header. 5 | Philippe Arteau (h3xStream) 6 | https://github.com/GoSecure/csp-auditor 7 | 8 | 9 | 10 | org.zaproxy.zap.extension.cspauditor.CspAuditorExtension 11 | 12 | 13 | 14 | org.zaproxy.zap.extension.cspauditor.CspAuditorPassiveScanner 15 | 16 | 17 | 18 | 2.4.3 19 | 20 | 21 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/demo.gif -------------------------------------------------------------------------------- /demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/demo2.gif -------------------------------------------------------------------------------- /downloads/csp-auditor-burp-1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/downloads/csp-auditor-burp-1.jar -------------------------------------------------------------------------------- /downloads/cspauditor-alpha-1.zap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/downloads/cspauditor-alpha-1.zap -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoSecure/csp-auditor/83a2e35545973d226a2d8eb5148ad73673f34557/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'csp-auditor' 2 | include 'csp-auditor-core' 3 | include 'csp-auditor-burp-plugin' 4 | include 'csp-auditor-zap-plugin' 5 | --------------------------------------------------------------------------------