24use Psr\Log\LoggerInterface;
46 $logname = $reportOnly ?
'csp-report-only' :
'csp';
47 $this->log = LoggerFactory::getInstance( $logname );
48 $userAgent = $this->
getRequest()->getHeader(
'user-agent' );
52 $flags = $this->
getFlags( $report, $userAgent );
57 'csp-report' => $report,
58 'method' => __METHOD__,
59 'user_id' => $this->
getUser()->getId() ?:
'logged-out',
60 'user-agent' => $userAgent,
73 if ( in_array(
'false-positive', $flags ) ) {
75 $this->log->debug( $logLine,
$context );
78 $this->log->warning( $logLine,
$context );
89 private function getFlags( $report, $userAgent ) {
92 $falsePositives = $this->
getConfig()->get(
'CSPFalsePositiveUrls' );
99 $flags[] =
'report-only';
105 $report[
'blocked-uri'] ===
"self"
108 isset( $report[
'blocked-uri'] ) &&
112 isset( $report[
'source-file'] ) &&
119 $flags[] =
'false-positive';
130 if ( isset( $patterns[ $url ] ) ) {
135 unset( $bits[
'user'], $bits[
'pass'], $bits[
'query'], $bits[
'fragment'] );
138 if ( isset( $patterns[$serverUrl] ) ) {
143 foreach ( $patterns as $pattern => $val ) {
147 if ( substr( $pattern, -1 ) ===
'/' && strpos( $url, $pattern ) === 0 ) {
162 $contentType = $req->getHeader(
'content-type' );
163 if ( $contentType !==
'application/json'
164 && $contentType !==
'application/csp-report'
166 $this->
error(
'wrongformat', __METHOD__ );
168 if ( $req->getHeader(
'content-length' ) > self::MAX_POST_SIZE ) {
169 $this->
error(
'toobig', __METHOD__ );
179 $postBody = $this->
getRequest()->getRawInput();
180 if ( strlen( $postBody ) > self::MAX_POST_SIZE ) {
182 $this->
error(
'toobig', __METHOD__ );
184 $status = FormatJson::parse( $postBody, FormatJson::FORCE_ASSOC );
185 if ( !$status->isGood() ) {
186 $msg = $status->getErrors()[0][
'message'];
187 if ( $msg instanceof
Message ) {
188 $msg = $msg->getKey();
190 $this->
error( $msg, __METHOD__ );
193 $report = $status->getValue();
195 if ( !isset( $report[
'csp-report'] ) ) {
196 $this->
error(
'missingkey', __METHOD__ );
198 return $report[
'csp-report'];
211 $flagText =
'[' . implode(
', ', $flags ) .
']';
214 $blockedOrigin = isset( $report[
'blocked-uri'] )
217 $page = $report[
'document-uri'] ??
'n/a';
218 $line = isset( $report[
'line-number'] )
219 ?
':' . $report[
'line-number']
221 $warningText = $flagText .
222 ' Received CSP report: <' . $blockedOrigin .
'>' .
223 ' blocked from being loaded on <' . $page .
'>' .
$line;
233 unset( $bits[
'user'], $bits[
'pass'], $bits[
'query'], $bits[
'fragment'] );
247 private function error( $code, $method ) {
248 $this->log->info(
'Error reading CSP report: ' . $code, [
250 'user-agent' => $this->
getRequest()->getHeader(
'user-agent' )
254 [
'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.
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
getParameter( $paramName, $parseLimit=true)
Get a value for the given 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.
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 deals with fetching and processing of interface message into a variety of formats.