Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 105 additions & 13 deletions assets/app/components/FindingItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,111 @@ const STATUS_ICONS = {
skipped: '–',
};

function renderEvidenceValue( value ) {
if ( Array.isArray( value ) ) {
return value.join( ', ' ) || __( '(empty)', 'wp-security' );
function cellText( cell ) {
if ( cell === null || cell === undefined || cell === '' ) {
return '—';
}
if ( value !== null && typeof value === 'object' ) {
return JSON.stringify( value );
if ( typeof cell === 'object' ) {
return JSON.stringify( cell );
}
return String( cell );
}

function EvidenceValue( { type, value } ) {
switch ( type ) {
case 'empty':
return (
<span className="wpsec-finding__evidence-empty">
{ __( 'None', 'wp-security' ) }
</span>
);

case 'boolean':
return (
<span
className="wpsec-finding__evidence-boolean"
data-value={ value ? 'yes' : 'no' }
>
{ value ? __( 'Yes', 'wp-security' ) : __( 'No', 'wp-security' ) }
</span>
);

case 'list':
if ( ! Array.isArray( value ) || value.length === 0 ) {
return (
<span className="wpsec-finding__evidence-empty">
{ __( 'None', 'wp-security' ) }
</span>
);
}
return (
<ul className="wpsec-finding__evidence-list">
{ value.map( ( item, index ) => (
// eslint-disable-next-line react/no-array-index-key
<li key={ index }>{ cellText( item ) }</li>
) ) }
</ul>
);

case 'table':
if ( ! value || ! Array.isArray( value.rows ) || value.rows.length === 0 ) {
return (
<span className="wpsec-finding__evidence-empty">
{ __( 'None', 'wp-security' ) }
</span>
);
}
return (
<table className="wpsec-finding__evidence-subtable">
<thead>
<tr>
{ value.columns.map( ( column ) => (
<th key={ column.key }>{ column.label }</th>
) ) }
</tr>
</thead>
<tbody>
{ value.rows.map( ( row, index ) => (
// eslint-disable-next-line react/no-array-index-key
<tr key={ index }>
{ value.columns.map( ( column ) => (
<td key={ column.key }>{ cellText( row[ column.key ] ) }</td>
) ) }
</tr>
) ) }
</tbody>
</table>
);

case 'group':
if ( ! Array.isArray( value ) || value.length === 0 ) {
return (
<span className="wpsec-finding__evidence-empty">
{ __( 'None', 'wp-security' ) }
</span>
);
}
return (
<dl className="wpsec-finding__evidence-group">
{ value.map( ( item ) => (
<div className="wpsec-finding__evidence-group-row" key={ item.key }>
<dt>{ item.label }</dt>
<dd>
<EvidenceValue type={ item.type } value={ item.value } />
</dd>
</div>
) ) }
</dl>
);

case 'scalar':
default:
return <span>{ cellText( value ) }</span>;
}
return String( value ?? '' );
}

function EvidenceTable( { evidence } ) {
const entries = Object.entries( evidence );
if ( entries.length === 0 ) {
if ( ! Array.isArray( evidence ) || evidence.length === 0 ) {
return null;
}

Expand All @@ -31,11 +123,11 @@ function EvidenceTable( { evidence } ) {
</summary>
<table className="wpsec-finding__evidence-table">
<tbody>
{ entries.map( ( [ key, value ] ) => (
<tr key={ key } className="wpsec-finding__evidence-row">
<th className="wpsec-finding__evidence-key">{ key }</th>
{ evidence.map( ( item ) => (
<tr key={ item.key } className="wpsec-finding__evidence-row">
<th className="wpsec-finding__evidence-key">{ item.label }</th>
<td className="wpsec-finding__evidence-value">
{ renderEvidenceValue( value ) }
<EvidenceValue type={ item.type } value={ item.value } />
</td>
</tr>
) ) }
Expand All @@ -47,7 +139,7 @@ function EvidenceTable( { evidence } ) {

export function FindingItem( { finding } ) {
const hasEvidence =
finding.evidence && Object.keys( finding.evidence ).length > 0;
Array.isArray( finding.evidence ) && finding.evidence.length > 0;

return (
<li className="wpsec-finding">
Expand Down
71 changes: 71 additions & 0 deletions assets/app/styles/_server.scss
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,77 @@
word-break: break-all;
}

.wpsec-finding__evidence-empty {
color: $wpsec-color-muted;
font-style: italic;
}

.wpsec-finding__evidence-boolean {
display: inline-flex;
align-items: center;
padding: 1px $wpsec-spacing-sm;
border-radius: $wpsec-radius-sm;
font-size: $wpsec-font-sm;
font-weight: 600;

&[data-value='yes'] { background: $wpsec-color-primary-light; color: $wpsec-color-primary; }
&[data-value='no'] { background: $wpsec-color-bg; color: $wpsec-color-muted; }
}

.wpsec-finding__evidence-list {
margin: 0;
padding-left: $wpsec-spacing-md;
list-style: disc;

li {
padding: 1px 0;
}
}

.wpsec-finding__evidence-subtable {
width: 100%;
border-collapse: collapse;
font-size: $wpsec-font-sm;

th,
td {
padding: 3px $wpsec-spacing-sm 3px 0;
text-align: left;
vertical-align: top;
}

th {
color: $wpsec-color-text;
font-weight: 600;
border-bottom: 1px solid $wpsec-color-border;
}

td {
color: $wpsec-color-muted;
}
}

.wpsec-finding__evidence-group {
margin: 0;
}

.wpsec-finding__evidence-group-row {
display: flex;
gap: $wpsec-spacing-sm;
padding: 1px 0;

dt {
font-weight: 600;
color: $wpsec-color-text;
white-space: nowrap;
}

dd {
margin: 0;
color: $wpsec-color-muted;
}
}

// Empty / error states.
.wpsec-server__empty {
color: $wpsec-color-muted;
Expand Down
Loading
Loading