44 $logname = $reportOnly ?
'csp-report-only' :
'csp';
45 $this->log = LoggerFactory::getInstance( $logname );
46 $userAgent = $this->
getRequest()->getHeader(
'user-agent' );
50 $flags = $this->
getFlags( $report, $userAgent );
55 'csp-report' => $report,
56 'method' => __METHOD__,
57 'user_id' => $this->
getUser()->getId() ?:
'logged-out',
58 'user-agent' => $userAgent,
71 if ( in_array(
'false-positive', $flags ) ) {
73 $this->log->debug( $logLine,
$context );
76 $this->log->warning( $logLine,
$context );
87 private function getFlags( $report, $userAgent ) {
90 $falsePositives = $this->
getConfig()->get(
'CSPFalsePositiveUrls' );
97 $flags[] =
'report-only';
103 $report[
'blocked-uri'] ===
"self"
106 isset( $report[
'blocked-uri'] ) &&
110 isset( $report[
'source-file'] ) &&
117 $flags[] =
'false-positive';
128 if ( isset( $patterns[ $url ] ) ) {
133 unset( $bits[
'user'], $bits[
'pass'], $bits[
'query'], $bits[
'fragment'] );
136 if ( isset( $patterns[$serverUrl] ) ) {
141 foreach ( $patterns as $pattern => $val ) {
145 if ( substr( $pattern, -1 ) ===
'/' && strpos( $url, $pattern ) === 0 ) {
160 $contentType = $req->getHeader(
'content-type' );
161 if ( $contentType !==
'application/json'
162 && $contentType !==
'application/csp-report'
164 $this->
error(
'wrongformat', __METHOD__ );
166 if ( $req->getHeader(
'content-length' ) > self::MAX_POST_SIZE ) {
167 $this->
error(
'toobig', __METHOD__ );
177 $postBody = $this->
getRequest()->getRawInput();
178 if ( strlen( $postBody ) > self::MAX_POST_SIZE ) {
180 $this->
error(
'toobig', __METHOD__ );
182 $status = FormatJson::parse( $postBody, FormatJson::FORCE_ASSOC );
183 if ( !$status->isGood() ) {
184 $msg = $status->getErrors()[0][
'message'];
185 if ( $msg instanceof
Message ) {
186 $msg = $msg->getKey();
188 $this->
error( $msg, __METHOD__ );
191 $report = $status->getValue();
193 if ( !isset( $report[
'csp-report'] ) ) {
194 $this->
error(
'missingkey', __METHOD__ );
196 return $report[
'csp-report'];
209 $flagText =
'[' . implode(
', ', $flags ) .
']';
212 $blockedOrigin = isset( $report[
'blocked-uri'] )
215 $page = $report[
'document-uri'] ??
'n/a';
216 $line = isset( $report[
'line-number'] )
217 ?
':' . $report[
'line-number']
219 $warningText = $flagText .
220 ' Received CSP report: <' . $blockedOrigin .
'>' .
221 ' blocked from being loaded on <' . $page .
'>' .
$line;
231 unset( $bits[
'user'], $bits[
'pass'], $bits[
'query'], $bits[
'fragment'] );
245 private function error( $code, $method ) {
246 $this->log->info(
'Error reading CSP report: ' . $code, [
248 'user-agent' => $this->
getRequest()->getHeader(
'user-agent' )
252 [
'apierror-csp-report',
wfEscapeWikiText( $code ) ],
'cspreport-' . $code, [], 400
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfAssembleUrl( $urlParts)
This function will reassemble a URL parsed with wfParseURL.
This abstract class implements many basic API functions, and is the base of all API classes.
const PARAM_REQUIRED
(boolean) Is the parameter required?
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
getResult()
Get the result object.
getModuleName()
Get the name of the module being executed by this instance.
Api module to receive and log CSP violation reports.
getReport()
Get the report from post body and turn into associative array.
matchUrlPattern( $url, array $patterns)
error( $code, $method)
Stop processing the request, and output/log an error.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
execute()
Logs a content-security-policy violation report from web browser.
shouldCheckMaxLag()
Doesn't touch db, so max lag should be rather irrelavent.
isReadMode()
Even if you don't have read rights, we still want your report.
logReport( $flags, $logLine, $context)
Log CSP report, with a different severity depending on $flags.
getFlags( $report, $userAgent)
Get extra notes about the report.
const MAX_POST_SIZE
These reports should be small.
mustBePosted()
Indicates whether this module must be called with a POST request.
isWriteMode()
Indicates whether this module requires write mode.
isInternal()
Mark as internal.
generateLogLine( $flags, $report)
Get text of log line.
verifyPostBodyOk()
Output an api error if post body is obviously not OK.
static falsePositiveBrowser( $ua)
Does this browser give false positive reports?
The Message class provides methods which fulfil two basic services: