Files

14 lines
9.8 KiB
JavaScript

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
import{property as b,state as h,customElement as $}from"lit/decorators.js";import{LitElement as v,html as s,nothing as d}from"lit";import{classMap as R}from"lit/directives/class-map.js";import p from"@typo3/core/ajax/ajax-request.js";import y from"@typo3/core/event/regular-event.js";import i from"~labels/backend.modules.content_security_policy";import"bootstrap";var a=function(r,e,t,l){var o=arguments.length,c=o<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,t):l,u;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(r,e,t,l);else for(var m=r.length-1;m>=0;m--)(u=r[m])&&(c=(o<3?u(c):o>3?u(e,t,c):u(e,t))||c);return o>3&&c&&Object.defineProperty(e,t,c),c},f;(function(r){r.fixable="fixable",r.irrelevant="irrelevant",r.suspicious="suspicious"})(f||(f={}));let n=class extends v{constructor(){super(...arguments),this.selectedScope=null,this.reports=[],this.selectedReport=null,this.suggestions=[],this.previouslyFocusedRow=null}connectedCallback(){super.connectedCallback(),this.fetchReports(),this.peripheralEvent=new y("click",(e,t)=>{t.dataset.cspReportsHandler==="refresh"&&(e.preventDefault(),this.fetchReports())}),this.peripheralEvent.delegateTo(document,"[data-csp-reports-handler]")}disconnectedCallback(){super.disconnectedCallback(),this.peripheralEvent?.release()}updated(e){super.updated(e),e.has("selectedReport")&&this.selectedReport!==null&&this.updateComplete.then(()=>{this.querySelector("#report-details")?.focus()})}createRenderRoot(){return this}render(){return s`<div class=infolist-container><div class=infolist><div class=infolist-header>${this.renderNavigation()}</div><div class=infolist-content><div class="table-fit mb-0"><table class="table table-striped"><thead><tr><th>${i.get("module.label.created")}</th><th>${i.get("module.label.scope")}</th><th>${i.get("module.label.violation")}</th><th>${i.get("module.label.uri")}</th><th></th></tr></thead><tbody>${this.reports.length===0?s`<tr><td colspan=5>${i.get("module.label.noEntriesAvailable")}</td></tr>`:d} ${this.reports.map(e=>s`<tr class=${R({"table-info":this.selectedReport===e})} data-mutation-group=${e.mutationHashes.join("-")} @click=${()=>this.selectReport(e)} @keydown=${t=>this.handleReportKeydown(t,e)} tabindex=0 role=button aria-label=${i.get("module.label.showDetails",[e.details.effectiveDirective,e.details.blockedUri])}><td>${e.created}</td><td>${e.scope}</td><td><span class="badge badge-warning">${e.count}</span> ${e.details.effectiveDirective}</td><td>${this.shortenUri(e.details.blockedUri)}</td><td>${e.attributes.join(", ")}</td></tr>`)}</tbody></table></div></div><div class=infolist-info${this.selectedReport?" infolist-info-showrecord":""}>${this.renderGuide()} ${this.renderSelectedReport()}</div></div></div>`}renderNavigation(){return s`<div class=btn-toolbar><button type=button class="btn btn-default dropdown-toggle" data-bs-toggle=dropdown aria-expanded=false title=${i.get("module.label.scope")}>${this.selectedScope===null?i.get("module.label.all"):this.selectedScope}</button><ul class=dropdown-menu><button class="dropdown-item dropdown-item-spaced" title=${i.get("module.label.all")} @click=${()=>this.selectScope(null)}><span class=${this.selectedScope===null?"text-primary":""}> <typo3-backend-icon identifier=${this.selectedScope===null?"actions-dot":"empty-empty"} size=small></typo3-backend-icon> </span>${i.get("module.label.all")}</button> ${this.scopes.map(e=>s`<li><button class="dropdown-item dropdown-item-spaced" title=${e} @click=${()=>this.selectScope(e)}><span class=${e===this.selectedScope?"text-primary":""}> <typo3-backend-icon identifier=${e===this.selectedScope?"actions-dot":"empty-empty"} size=small></typo3-backend-icon> </span>${e}</button></li>`)}</ul><button type=button class="btn btn-danger" title=${i.get("module.label.removeAll")} @click=${()=>this.invokeDeleteReportsAction()}>${i.get("module.label.removeAll")} ${this.selectedScope!==null?s`"${this.selectedScope}"`:d}</button></div>`}renderGuide(){return s`${this.selectedReport?d:s`<div class=infolist-info-norecord><div class="card mb-0"><div class=card-body><p>${i.get("module.label.guide.no_record_selected")}</p></div></div></div>`}`}renderSelectedReport(){const e=this.selectedReport;return s`${e?s`<div class=infolist-info-record><div class="card mb-0" tabindex=-1 id=report-details @keydown=${t=>this.handleDetailsKeydown(t)}><div class=card-header><h3>${i.get("module.label.details")}</h3></div><div class=card-body><dl><dt>${i.get("module.label.directive")} / ${i.get("module.label.disposition")}</dt><dd>${e.details.effectiveDirective} / ${e.details.disposition}</dd><dt>${i.get("module.label.document_uri")}</dt><dd>${e.details.documentUri} ${this.renderCodeLocation(e)}</dd>${e.details.sourceFile&&e.details.sourceFile!==e.details.documentUri?s`<dt>${i.get("module.label.source_file")}</dt><dd>${e.details.sourceFile}</dd>`:d}<dt>${i.get("module.label.blocked_uri")}</dt><dd>${e.details.blockedUri}</dd>${e.details.scriptSample?s`<dt>${i.get("module.label.sample")}</dt><dd><code>${e.details.scriptSample}</code></dd>`:d} ${e.meta.agent?s`<dt>${i.get("module.label.user_agent")}</dt><dd><code>${e.meta.agent}</code></dd>`:d}<dt>${i.get("module.label.uuid")}</dt><dd><code>${e.uuid}</code></dd><dt>${i.get("module.label.summary")}</dt><dd><code>${e.summary}</code></dd></dl></div>${this.suggestions.length>0?s`<div class=card-header><h3>${i.get("module.label.suggestions")}</h3></div>`:d} ${this.suggestions.map(t=>s`<div class=card-body><h4>${t.label||t.identifier}</h4>${t.collection.mutations.map(l=>s`<p><i>${l.mode}</i> <code>${l.directive}: ${l.sources.join(" ")}</code></p>`)} <button class="btn btn-primary" @click=${()=>this.invokeMutateReportAction(e,t)}><typo3-backend-icon identifier=actions-check size=small></typo3-backend-icon>${i.get("module.button.apply")}</button></div>`)}<div class=card-footer><button class="btn btn-default" @click=${()=>this.selectReport(null)}><typo3-backend-icon identifier=actions-close size=small></typo3-backend-icon>${i.get("module.button.close")}</button> <button class="btn btn-default" @click=${()=>this.invokeMuteReportAction(e)}><typo3-backend-icon identifier=actions-ban size=small></typo3-backend-icon>${i.get("module.button.mute")}</button> <button class="btn btn-default" @click=${()=>this.invokeDeleteReportAction(e)}><typo3-backend-icon identifier=actions-delete size=small></typo3-backend-icon>${i.get("module.button.delete")}</button></div></div></div>`:d}`}renderCodeLocation(e){if(!e.details.lineNumber)return d;const t=[e.details.lineNumber];return e.details.columnNumber&&t.push(e.details.columnNumber),s`(${t.join(":")})`}selectReport(e){this.suggestions=[],e!==null&&this.selectedReport!==e?(this.previouslyFocusedRow=document.activeElement,this.selectedReport=e,this.invokeHandleReportAction(e).then(t=>this.suggestions=t)):(this.selectedReport=null,this.previouslyFocusedRow&&this.updateComplete.then(()=>{this.previouslyFocusedRow?.focus(),this.previouslyFocusedRow=null}))}handleReportKeydown(e,t){if(e.key==="Enter"||e.key==="Space"||e.key===" "){e.preventDefault(),this.selectReport(t);return}const l=e.currentTarget;let o=null;switch(e.key){case"ArrowDown":e.preventDefault(),o=l.nextElementSibling;break;case"ArrowUp":e.preventDefault(),o=l.previousElementSibling;break;case"Home":e.preventDefault(),o=l.parentElement?.firstElementChild;break;case"End":e.preventDefault(),o=l.parentElement?.lastElementChild;break;default:return}o&&o.hasAttribute("tabindex")&&o.focus()}handleDetailsKeydown(e){e.key==="Escape"&&(e.preventDefault(),this.selectReport(null))}focusFirstReportRow(){this.updateComplete.then(()=>{this.querySelector('tbody tr[tabindex="0"]')?.focus()})}selectScope(e){this.selectedScope=e,this.fetchReports()}fetchReports(){this.invokeFetchReportsAction().then(e=>this.reports=e)}filterReports(...e){e.includes(this.selectedReport?.uuid)&&(this.selectedReport=null),this.reports=this.reports.filter(t=>!e.includes(t.uuid))}invokeFetchReportsAction(){return new p(this.controlUri).post({action:"fetchReports",scope:this.selectedScope||""}).then(e=>e.resolve("application/json"))}invokeHandleReportAction(e){return new p(this.controlUri).post({action:"handleReport",uuid:e.uuid}).then(t=>t.resolve("application/json"))}invokeMutateReportAction(e,t){const l=this.reports.filter(o=>o.mutationHashes.includes(t.hash)).map(o=>o.summary);return new p(this.controlUri).post({action:"mutateReport",scope:e.scope,hmac:t.hmac,suggestion:t,summaries:l}).then(o=>o.resolve("application/json")).then(o=>this.filterReports(...o.uuids))}invokeMuteReportAction(e){new p(this.controlUri).post({action:"muteReport",summaries:[e.summary]}).then(t=>t.resolve("application/json")).then(t=>this.filterReports(...t.uuids)).then(()=>this.focusFirstReportRow())}invokeDeleteReportAction(e){new p(this.controlUri).post({action:"deleteReport",summaries:[e.summary]}).then(t=>t.resolve("application/json")).then(t=>this.filterReports(...t.uuids)).then(()=>this.focusFirstReportRow())}invokeDeleteReportsAction(){new p(this.controlUri).post({action:"deleteReports",scope:this.selectedScope||""}).then(e=>e.resolve("application/json")).then(()=>this.fetchReports()).then(()=>this.selectReport(null))}shortenUri(e){if(e==="inline")return e;try{return new URL(e).hostname}catch{return e}}};a([b({type:Array})],n.prototype,"scopes",void 0),a([b({type:String})],n.prototype,"controlUri",void 0),a([h()],n.prototype,"selectedScope",void 0),a([h()],n.prototype,"reports",void 0),a([h()],n.prototype,"selectedReport",void 0),a([h()],n.prototype,"suggestions",void 0),n=a([$("typo3-backend-security-csp-reports")],n);export{n as CspReports};