Skip to content

Commit

Permalink
feat: allow rules to be suppressed from reports
Browse files Browse the repository at this point in the history
  • Loading branch information
sknep committed Oct 18, 2024
1 parent cd4f911 commit 786430f
Show file tree
Hide file tree
Showing 15 changed files with 379 additions and 182 deletions.
42 changes: 18 additions & 24 deletions api/controllers/build-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,29 +101,11 @@ module.exports = wrapHandlers({
const { params, user } = req;
const { task_id: taskId, sub_page: subPage } = params;

if (process.env.FEATURE_LOCAL_BUILD_REPORTS === 'active') {
const task = localSiteBuildTasks.find(t => t.id.toString() === taskId);
const file = subPage || 'index';
const reportPath = join(
__dirname,
`../../services/local/tasks/${task.type}/${file}.json`
);

const raw = await readFile(reportPath, 'utf-8');
const report = JSON.parse(raw);

return res.json({
type: task.type,
siteId: task.id,
buildId: task.id,
report,
});
}

const task = await BuildTask.findOne({
where: { id: taskId },
include: [
BuildTaskType,
SiteBuildTask,
{
model: Build,
include: Site,
Expand All @@ -137,15 +119,27 @@ module.exports = wrapHandlers({
// return identical responses for missing tasks and unauthorized tasks
return res.notFound();
}

const taskJSON = buildTaskSerializer.serialize(task);

// add report data to the task
let report = null;
const key = `${task.artifact}${subPage || 'index'}.json`;
const reportReponse = await getObject(task.Build.Site, key);
const reportString = await reportReponse.Body.transformToString();
report = JSON.parse(reportString);
// for local seeded reports
if (process.env.FEATURE_LOCAL_BUILD_REPORTS === 'active') {
const localTask = localSiteBuildTasks.find(t => t.id.toString() === taskId);
const file = subPage || 'index';
const reportPath = join(
__dirname,
`../../services/local/tasks/${localTask.type}/${file}.json`
);

const raw = await readFile(reportPath, 'utf-8');
report = JSON.parse(raw);
} else {
const key = `${task.artifact}${subPage || 'index'}.json`;
const reportReponse = await getObject(task.Build.Site, key);
const reportString = await reportReponse.Body.transformToString();
report = JSON.parse(reportString);
}

const type = appMatch(task.BuildTaskType);

Expand Down
19 changes: 17 additions & 2 deletions api/serializers/build-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@ const BaseSerializer = require('./base');
const bttSerializer = require('./build-task-type');

function miniBuildSerializer({
branch, clonedCommitSha, requestedCommitSha, username, user, createdAt,
branch,
clonedCommitSha,
requestedCommitSha,
username,
user,
createdAt,
}) {
return {
branch, clonedCommitSha, requestedCommitSha, username, user, createdAt,
branch,
clonedCommitSha,
requestedCommitSha,
username,
user,
createdAt,
};
}

function miniSiteBuildTaskSerializer({ id, branch, metadata }) {
return { id, branch, metadata };
}
const attributes = {
id: '',
artifact: '',
Expand All @@ -17,6 +31,7 @@ const attributes = {
createdAt: 'date',
updatedAt: 'date',
siteBuildTaskId: '',
SiteBuildTask: sbt => sbt && miniSiteBuildTaskSerializer(sbt.toJSON()),
BuildTaskType: btt => btt && bttSerializer.serialize(btt),
Build: build => build && miniBuildSerializer(build.toJSON()),
buildId: '',
Expand Down
23 changes: 21 additions & 2 deletions frontend/pages/reports/ShowReport.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,31 @@ export default function Report() {
if (!data) return <ReportNotFound />;

const { report, siteId, buildId } = data;
const sbtCustomRules = data.SiteBuildTask?.metadata?.rules || [];
const sbtId = data.SiteBuildTask?.id || null;

switch (data.type) {
case 'owasp-zap':
return <Zap data={report} siteId={siteId} buildId={buildId} />;
case 'zap':
return (
<Zap
siteId={siteId}
buildId={buildId}
sbtId={sbtId}
sbtType={data.type}
report={report}
sbtCustomRules={sbtCustomRules}
/>
);
case 'a11y':
return <A11yScanIndex data={report} siteId={siteId} buildId={buildId} />;
return (
<A11yScanIndex
siteId={siteId}
buildId={buildId}
report={report}
sbtType={data.type}
/>
);
default:
return <ReportNotFound />;
}
Expand Down
15 changes: 13 additions & 2 deletions frontend/pages/reports/ShowReportSubPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,20 @@ export default function Report() {
if (!data) return <ReportNotFound />;

const { report, siteId, buildId } = data;
const sbtCustomRules = data.SiteBuildTask?.metadata?.rules || [];
const sbtId = data.SiteBuildTask?.id || null;

if (data.type === 'a11y') {
return <A11yScanChild data={report} siteId={siteId} buildId={buildId} />;
if (data.type === 'a11y' && report) {
return (
<A11yScanChild
siteId={siteId}
buildId={buildId}
sbtId={sbtId}
sbtType={data.type}
report={report}
sbtCustomRules={sbtCustomRules}
/>
);
}

return <ReportNotFound />;
Expand Down
43 changes: 25 additions & 18 deletions frontend/pages/reports/components/A11yScanChildLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import ScanFindings from './ScanFindings';
import BackToTopButton from './BackToTopButton';
import About from './about';

export default function A11yScanChild({ data, siteId, buildId }) {
export default function A11yScanChild({
report, sbtCustomRules = [], siteId, buildId, sbtId, sbtType,
}) {
const scanTitle = 'Accessibility';
const pageTitle = `Pages | ${scanTitle} report for ${data.url} on ${datetime.dateAndTimeSimple(data.timestamp)} for build id ${buildId}`;

const allResults = Object.values(data.groupedViolations).flat(1);
const pageTitle = `Pages | ${scanTitle} report for ${report.url} on ${datetime.dateAndTimeSimple(report.timestamp)} for build id ${buildId}`;
const allResults = Object.values(report.groupedViolations).flat(1);
const ignoreFn = finding => finding.ignore || (utils.getSeverityThemeToken(finding.impact, 'a11y') == null);
// const suppressed = allResults.filter(ignoreFn);
const unsuppressed = allResults.filter(r => !ignoreFn(r));
Expand All @@ -22,14 +23,14 @@ export default function A11yScanChild({ data, siteId, buildId }) {
...group,
label: group.label,
usePill: true,
count: data.groupedViolations[group?.name]?.length || 0,
count: report.groupedViolations[group?.name]?.length || 0,
})
);
navGroups.push(
// TODO: split into suppressed/unsuppressed items
{
label: 'Total results',
count: data?.violationsCount,
count: report?.violationsCount,
},
{
label: 'All unsuppressed results',
Expand All @@ -38,7 +39,7 @@ export default function A11yScanChild({ data, siteId, buildId }) {
},
{
label: 'Total passes',
count: data?.passes?.length,
count: report?.passes?.length,
}
);

Expand All @@ -53,9 +54,9 @@ export default function A11yScanChild({ data, siteId, buildId }) {
Accessibility report for
{' '}
<br />
<span className="font-code-lg text-normal text-primary-darker bg-accent-cool-lighter padding-x-05r narrow-mono break-anywhere">{data.url}</span>
<span className="font-code-lg text-normal text-primary-darker bg-accent-cool-lighter padding-x-05r narrow-mono break-anywhere">{report.url}</span>
<span className="font-sans-lg text-normal margin-left-1">
<a className="usa-link font-body-xs text-no-wrap margin-x-2" target="_blank" aria-label="open scanned page in a new window," title="open scanned page in a new window" href={data.url} rel="noreferrer">
<a className="usa-link font-body-xs text-no-wrap margin-x-2" target="_blank" aria-label="open scanned page in a new window," title="open scanned page in a new window" href={report.url} rel="noreferrer">
open page
<svg className="usa-icon text-ttop" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
<path fill="currentColor" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z" />
Expand All @@ -72,7 +73,7 @@ export default function A11yScanChild({ data, siteId, buildId }) {
<div className="grid-row border-top-1px padding-top-1">
<section className="tablet:grid-col-auto">
<ScanNav
generated={datetime.dateAndTimeSimple(data.timestamp)}
generated={datetime.dateAndTimeSimple(report.timestamp)}
buildId={buildId}
siteId={siteId}
groups={navGroups}
Expand Down Expand Up @@ -114,19 +115,21 @@ export default function A11yScanChild({ data, siteId, buildId }) {
<div>
<ScanFindings
siteId={siteId}
scanType="a11y"
count={data.violationsCount}
groupedFindings={data.groupedViolations}
sbtId={sbtId}
sbtType={sbtType}
sbtCustomRules={sbtCustomRules}
count={report.violationsCount}
groupedFindings={report.groupedViolations}
/>
</div>
<div>
<A11yPassed passes={data.passes} />
<A11yPassed passes={report.passes} />
<hr />
<About scanType="a11y" siteId={siteId}>
<About sbtType={sbtType} siteId={siteId}>
<p className="font-body-xs line-height-sans-3">
This report was generated for
{' '}
<code className="narrow-mono font-mono-2xs bg-accent-cool-lighter">{data.url}</code>
<code className="narrow-mono font-mono-2xs bg-accent-cool-lighter">{report.url}</code>
{' from '}
<Link reloadDocument to={`/sites/${siteId}/builds/${buildId}/logs`} className="usa-link">
build #
Expand All @@ -135,7 +138,7 @@ export default function A11yScanChild({ data, siteId, buildId }) {
{' '}
scanned on
{' '}
{datetime.dateAndTimeSimple(data.timestamp)}
{datetime.dateAndTimeSimple(report.timestamp)}
</p>
</About>
</div>
Expand Down Expand Up @@ -202,7 +205,11 @@ A11yPassed.propTypes = {

A11yScanChild.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
data: PropTypes.object.isRequired,
report: PropTypes.object.isRequired,
// eslint-disable-next-line react/forbid-prop-types
sbtCustomRules: PropTypes.array,
siteId: PropTypes.number.isRequired,
sbtId: PropTypes.number.isRequired,
sbtType: PropTypes.string.isRequired,
buildId: PropTypes.number.isRequired,
};
29 changes: 15 additions & 14 deletions frontend/pages/reports/components/A11yScanIndexLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import ScanPagePathAndReportLink from './ScanPagePathReportLink';
import ScanFindingsSummary from './ScanFindingsSummary';
import About from './about';

export default function A11yScanIndex({ data, siteId, buildId }) {
export default function A11yScanIndex({ report, siteId, buildId }) {
const scanTitle = 'Accessibility';
const pageTitle = `Pages | ${scanTitle} report index for ${data.baseurl} on ${datetime.dateAndTimeSimple(data.reportPages[0].timestamp)} for build id #${buildId}`;
const pageTitle = `Pages | ${scanTitle} report index for ${report.baseurl} on ${datetime.dateAndTimeSimple(report.reportPages[0].timestamp)} for build id #${buildId}`;
function findReportsPerURLs(url) {
return data.reportPages.find(page => page.absoluteURL === url)?.path || '';
return report.reportPages.find(page => page.absoluteURL === url)?.path || '';
}
const summarizedResults = [...data.violatedRules].map(result => ({
const summarizedResults = [...report.violatedRules].map(result => ({
...result,
name: result.help,
ref: result.helpUrl,
Expand All @@ -39,7 +39,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
{' '}
<br />
<span className="font-code-lg text-normal text-primary-darker bg-accent-cool-lighter padding-x-05r narrow-mono break-anywhere">
{data.baseurl}
{report.baseurl}
{' '}
</span>
<span className="text-italic font-sans-lg text-normal margin-left-2">(all pages)</span>
Expand Down Expand Up @@ -84,7 +84,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
{' '}
<span className="font-body-lg text-secondary-vivid">
(
{data.violatedRules.length}
{report.violatedRules.length}
)
</span>
</h2>
Expand All @@ -93,7 +93,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
<div className="grid-row">
<div className="grid-col">
<ScanFindingsSummary
baseurl={data.baseurl}
baseurl={report.baseurl}
suppressedFindings={suppressed}
unsuppressedFindings={unsuppressed}
/>
Expand All @@ -106,7 +106,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
{' '}
<span className="font-body-lg text-accent-cool-darker">
(
{data.reportPages.length}
{report.reportPages.length}
)
</span>
</h2>
Expand All @@ -115,8 +115,8 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
<div className="grid-row">
<div className="grid-col">
<ScanResultsChildPages
pages={data.reportPages}
baseurl={data.baseurl}
pages={report.reportPages}
baseurl={report.baseurl}
/>
<p className="font-body-2xs line-height-body-3">
This report was generated for
Expand All @@ -128,7 +128,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
{' '}
scanned on
{' '}
{datetime.dateAndTimeSimple(data.reportPages[0]?.timestamp)}
{datetime.dateAndTimeSimple(report.reportPages[0]?.timestamp)}
.
</p>
</div>
Expand All @@ -139,7 +139,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
<p className="font-body-xs line-height-body-3">
This report was generated for
{' '}
<code className="narrow-mono font-mono-2xs bg-accent-cool-lighter">{data.baseurl}</code>
<code className="narrow-mono font-mono-2xs bg-accent-cool-lighter">{report.baseurl}</code>
{' from '}
<Link reloadDocument to={`/sites/${siteId}/builds/${buildId}/logs`} className="usa-link">
build #
Expand All @@ -148,7 +148,7 @@ export default function A11yScanIndex({ data, siteId, buildId }) {
{' '}
scanned on
{' '}
{datetime.dateAndTimeSimple(data.reportPages[0]?.timestamp)}
{datetime.dateAndTimeSimple(report.reportPages[0]?.timestamp)}
</p>
</About>
</div>
Expand Down Expand Up @@ -243,7 +243,8 @@ ScanResultsChildPages.propTypes = {

A11yScanIndex.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
data: PropTypes.object.isRequired,
report: PropTypes.object.isRequired,
// eslint-disable-next-line react/forbid-prop-types
siteId: PropTypes.number.isRequired,
buildId: PropTypes.number.isRequired,
};
Loading

0 comments on commit 786430f

Please sign in to comment.