def parse(self, sourceExpressionString): """Parses the given 'sourceExpressionString' according to the parameters set in this object. Returns the appropriate type of SourceExpression, or SourceExpression.INVALID() in case of a parsing error.""" sourceExpressionString = sourceExpressionString.strip() if sourceExpressionString.lower() == "'unsafe-eval'": return SourceExpression.UNSAFE_EVAL() elif sourceExpressionString.lower() == "'unsafe-inline'": return SourceExpression.UNSAFE_INLINE() elif sourceExpressionString.lower() == "'self'": return SelfSourceExpression.SELF() elif sourceExpressionString == "*": return URISourceExpression(None, "*", None, None) sourceExpressionParsedAsURI = self._parser.parse( sourceExpressionString) if sourceExpressionParsedAsURI == URI.EMPTY( ) or sourceExpressionParsedAsURI == URI.INVALID(): return SourceExpression.INVALID() if sourceExpressionParsedAsURI.getScheme( ) is not None and sourceExpressionParsedAsURI.getScheme().lower( ) not in self._knownSchemes: return SourceExpression.INVALID() port = sourceExpressionParsedAsURI.getPort() if port not in ('*', None): # convert port if it should be a number port = int(sourceExpressionParsedAsURI.getPort()) return URISourceExpression(sourceExpressionParsedAsURI.getScheme(), sourceExpressionParsedAsURI.getHost(), port, sourceExpressionParsedAsURI.getPath())
def parseJsonDict(self, jsonReport): """ Parses the given 'jsonReport' according to the parameters set in the constructor of this ReportParser and returns a Report object. 'jsonReport' is expected to be a Python dict object with attribute names and values corresponding to the definition of CSP violation reports. If 'jsonReport' cannot be parsed because it is syntactically invalid (or empty), Report.INVALID() will be returned. Depending on the configuration of this ReportParser object, some attributes will be parsed to replace their plain string values with a more high-level object representation. """ # replace names renamedReport = dict(map(lambda (key, val): (self._replaceName(key), val), jsonReport.iteritems())) # convert data in report convertedReport = {} deferredSelfURIs = set([]) # all key names that have URIs that are exactly 'self' (handle after parsing everything else) for (key, value) in renamedReport.iteritems(): if key in self._uriKeys: if value.lower().strip() == "self": deferredSelfURIs.add(key) continue else: value = self._uriParser.parse(value) elif key in self._directiveKeys: value = self._directiveParser.parse(value) elif key in self._policyKeys: value = self._policyParser.parse(value) if value in (URI.INVALID(), Directive.INVALID(), Policy.INVALID()): if self._strict: return Report.INVALID() else: continue convertedReport[key] = value # handle deferred parsing of 'self' URIs (they represent the document-uri) for key in deferredSelfURIs: if "document-uri" in self._uriKeys and "document-uri" in convertedReport: convertedReport[key] = convertedReport["document-uri"] elif self._strict: return Report.INVALID() for requiredKey in self._requiredKeys: if not requiredKey in convertedReport: return Report.INVALID() return Report(convertedReport)
def generateDirective(self, reportType, blockedURI): """ Generates a new Directive that allows exactly the kind of event that caused the CSP violation report, assuming this Directive ('self') was the 'violated-directive' in a report of the given 'reportType' (permitted values are 'regular', 'eval', and 'inline'), and 'blockedURI' was the value of the report field 'blocked-uri'. This directive may not be of the type 'default-src'. The result of this method is a Directive. It is Directive.INVALID() if (1) the type of this Directive is 'default-src' (or it is Directive.INVALID()), or a special type incompatible with 'reportType', (2) if 'reportType' is none out of 'regular', 'inline' or 'eval', (3) the 'blocked-uri' is URI.INVALID() or not a regular URI in the 'reportType'=='regular' case [old], or a regular URI in the 'eval' or 'inline' cases[/old]. This implementation does not handle URIs in any special way. That is, it does not add or remove ports, path/query components, or replace them with the 'self' keyword. """ if (self == Directive.INVALID() or (self == Directive.EVAL_SCRIPT_BASE_RESTRICTION() and reportType != 'eval') or (self == Directive.INLINE_SCRIPT_BASE_RESTRICTION() and reportType != 'inline') or (self == Directive.INLINE_STYLE_BASE_RESTRICTION() and reportType != 'inline') or self.getType() == "default-src" or reportType not in ('regular', 'eval', 'inline') or blockedURI == URI.INVALID() or (reportType == 'regular' and not blockedURI.isRegularURI()) # or (reportType in ('eval', 'inline') and blockedURI.isRegularURI()) or (reportType == 'eval' and self.getType() != 'script-src') or (reportType == 'inline' and self.getType() not in ('script-src', 'style-src'))): return Directive.INVALID() generated = Directive.INVALID() if reportType == 'regular': generated = Directive(self.getType(), (URISourceExpression( blockedURI.getScheme(), blockedURI.getHost(), blockedURI.getPort(), blockedURI.getPath()), )) elif reportType == 'eval': generated = Directive(self.getType(), (SourceExpression.UNSAFE_EVAL(), )) elif reportType == 'inline': generated = Directive(self.getType(), (SourceExpression.UNSAFE_INLINE(), )) return generated