2016-10-18 06:36:35 +01:00
|
|
|
// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2021-05-06 08:31:51 +01:00
|
|
|
package html
|
2016-10-18 06:36:35 +01:00
|
|
|
|
2021-05-06 08:31:51 +01:00
|
|
|
const templateContent = `
|
2016-10-18 06:36:35 +01:00
|
|
|
<!doctype html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
2021-05-21 10:14:43 +01:00
|
|
|
<title>Golang Security Checker</title>
|
2021-05-26 08:51:10 +01:00
|
|
|
<link rel="shortcut icon" type="image/png" href="https://securego.io/img/favicon.png">
|
2021-05-25 09:10:42 +01:00
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.2/css/bulma.min.css" integrity="sha512-byErQdWdTqREz6DLAA9pCnLbdoGGhXfU6gm1c8bkf7F51JVmUBlayGe2A31VpXWQP+eiJ3ilTAZHCR3vmMyybA==" crossorigin="anonymous"/>
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css" integrity="sha512-kZqGbhf9JTB4bVJ0G8HCkqmaPcRgo88F0dneK30yku5Y/dep7CZfCnNml2Je/sY4lBoqoksXz4PtVXS4GHSUzQ==" crossorigin="anonymous"/>
|
|
|
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js" integrity="sha512-s+tOYYcC3Jybgr9mVsdAxsRYlGNq4mlAurOrfNuGMQ/SCofNPu92tjE7YRZCsdEtWL1yGkqk15fU/ark206YTg==" crossorigin="anonymous"></script>
|
|
|
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/languages/go.min.js" integrity="sha512-+UYV2NyyynWEQcZ4sMTKmeppyV331gqvMOGZ61/dqc89Tn1H40lF05ACd03RSD9EWwGutNwKj256mIR8waEJBQ==" crossorigin="anonymous"></script>
|
2016-10-18 06:36:35 +01:00
|
|
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js" integrity="sha256-cLWs9L+cjZg8CjGHMpJqUgKKouPlmoMP/0wIdPtaPGs=" crossorigin="anonymous"></script>
|
|
|
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js" integrity="sha256-JIW8lNqN2EtqC6ggNZYnAdKMJXRQfkPMvdRt+b0/Jxc=" crossorigin="anonymous"></script>
|
|
|
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.17.0/babel.min.js" integrity="sha256-1IWWLlCKFGFj/cjryvC7GDF5wRYnf9tSvNVVEj8Bm+o=" crossorigin="anonymous"></script>
|
|
|
|
<style>
|
2021-05-25 09:10:42 +01:00
|
|
|
.field-label {
|
|
|
|
min-width: 80px;
|
|
|
|
}
|
|
|
|
.break-word {
|
|
|
|
word-wrap: break-word;
|
|
|
|
}
|
|
|
|
.help {
|
|
|
|
white-space: pre-wrap;
|
|
|
|
}
|
|
|
|
.tag {
|
|
|
|
width: 80px;
|
|
|
|
}
|
2016-10-18 06:36:35 +01:00
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<section class="section">
|
|
|
|
<div class="container">
|
|
|
|
<div id="content"></div>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<script>
|
|
|
|
var data = {{ . }};
|
|
|
|
</script>
|
|
|
|
<script type="text/babel">
|
|
|
|
var IssueTag = React.createClass({
|
|
|
|
render: function() {
|
2021-05-25 09:10:42 +01:00
|
|
|
var level = "tag"
|
2016-10-18 06:36:35 +01:00
|
|
|
if (this.props.level === "HIGH") {
|
2021-05-25 09:10:42 +01:00
|
|
|
level += " is-danger";
|
2021-05-21 07:13:20 +01:00
|
|
|
} else if (this.props.level === "MEDIUM") {
|
2021-05-25 09:10:42 +01:00
|
|
|
level += " is-warning";
|
|
|
|
} else if (this.props.level === "LOW") {
|
|
|
|
level += " is-info";
|
2016-10-18 06:36:35 +01:00
|
|
|
}
|
2021-05-25 09:10:42 +01:00
|
|
|
level +=" is-rounded";
|
2016-10-18 06:36:35 +01:00
|
|
|
return (
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="control">
|
|
|
|
<div className="tags has-addons">
|
|
|
|
<span className="tag is-dark is-rounded">{ this.props.label }</span>
|
|
|
|
<span className={ level }>{ this.props.level }</span>
|
|
|
|
</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2021-05-25 09:10:42 +01:00
|
|
|
var Highlight = React.createClass({
|
|
|
|
componentDidMount: function(){
|
|
|
|
var current = ReactDOM.findDOMNode(this);
|
|
|
|
hljs.highlightElement(current);
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<pre className="go"><code >{ this.props.code }</code></pre>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2016-10-18 06:36:35 +01:00
|
|
|
var Issue = React.createClass({
|
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<div className="issue box">
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="columns">
|
|
|
|
<div className="column is-three-quarters">
|
|
|
|
<strong className="break-word">{ this.props.data.file } (line { this.props.data.line })</strong>
|
|
|
|
<p>{ this.props.data.details }</p>
|
|
|
|
</div>
|
|
|
|
<div className="column is-one-quarter">
|
|
|
|
<div className="field is-grouped is-grouped-multiline">
|
|
|
|
<IssueTag label="Severity" level={ this.props.data.severity }/>
|
|
|
|
<IssueTag label="Confidence" level={ this.props.data.confidence }/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="highlight">
|
|
|
|
<Highlight code={ this.props.data.code }/>
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var Stats = React.createClass({
|
|
|
|
render: function() {
|
|
|
|
return (
|
2021-05-25 09:10:42 +01:00
|
|
|
<p className="help is-pulled-right">
|
2021-05-21 10:14:43 +01:00
|
|
|
Gosec {this.props.data.GosecVersion} scanned { this.props.data.Stats.files.toLocaleString() } files
|
2018-10-03 19:17:25 +01:00
|
|
|
with { this.props.data.Stats.lines.toLocaleString() } lines of code.
|
2021-05-20 10:34:52 +01:00
|
|
|
{ this.props.data.Stats.nosec ? '\n' + this.props.data.Stats.nosec.toLocaleString() + ' false positives (nosec) have been waived.' : ''}
|
2016-10-18 06:36:35 +01:00
|
|
|
</p>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var Issues = React.createClass({
|
|
|
|
render: function() {
|
2018-10-03 19:17:25 +01:00
|
|
|
if (this.props.data.Stats.files === 0) {
|
2016-10-18 06:36:35 +01:00
|
|
|
return (
|
|
|
|
<div className="notification">
|
|
|
|
No source files found. Do you even Go?
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2018-10-03 19:17:25 +01:00
|
|
|
if (this.props.data.Issues.length === 0) {
|
2016-10-18 06:36:35 +01:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<div className="notification">
|
|
|
|
Awesome! No issues found!
|
|
|
|
</div>
|
|
|
|
<Stats data={ this.props.data } />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2018-10-03 19:17:25 +01:00
|
|
|
var issues = this.props.data.Issues
|
2016-10-18 06:36:35 +01:00
|
|
|
.filter(function(issue) {
|
|
|
|
return this.props.severity.includes(issue.severity);
|
|
|
|
}.bind(this))
|
|
|
|
.filter(function(issue) {
|
|
|
|
return this.props.confidence.includes(issue.confidence);
|
|
|
|
}.bind(this))
|
|
|
|
.filter(function(issue) {
|
|
|
|
if (this.props.issueType) {
|
|
|
|
return issue.details.toLowerCase().startsWith(this.props.issueType.toLowerCase());
|
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}.bind(this))
|
|
|
|
.map(function(issue) {
|
|
|
|
return (<Issue data={issue} />);
|
|
|
|
}.bind(this));
|
|
|
|
if (issues.length === 0) {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<div className="notification">
|
|
|
|
No issues matched given filters
|
2018-10-03 19:17:25 +01:00
|
|
|
(of total { this.props.data.Issues.length } issues).
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
<Stats data={ this.props.data } />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="issues">
|
|
|
|
{ issues }
|
|
|
|
<Stats data={ this.props.data } />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var LevelSelector = React.createClass({
|
|
|
|
handleChange: function(level) {
|
|
|
|
return function(e) {
|
|
|
|
var updated = this.props.selected
|
|
|
|
.filter(function(item) { return item != level; });
|
|
|
|
if (e.target.checked) {
|
|
|
|
updated.push(level);
|
|
|
|
}
|
|
|
|
this.props.onChange(updated);
|
|
|
|
}.bind(this);
|
|
|
|
},
|
|
|
|
render: function() {
|
2018-10-03 19:17:25 +01:00
|
|
|
var HIGH = "HIGH", MEDIUM = "MEDIUM", LOW = "LOW";
|
2021-05-21 10:14:43 +01:00
|
|
|
var highDisabled = !this.props.available.includes(HIGH);
|
|
|
|
var mediumDisabled = !this.props.available.includes(MEDIUM);
|
|
|
|
var lowDisabled = !this.props.available.includes(LOW);
|
2016-10-18 06:36:35 +01:00
|
|
|
return (
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="field">
|
|
|
|
<div className="control">
|
|
|
|
<label className="checkbox" disabled={ highDisabled }>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={ this.props.selected.includes(HIGH) }
|
|
|
|
disabled={ highDisabled }
|
|
|
|
onChange={ this.handleChange(HIGH) }/> High
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div className="control">
|
|
|
|
<label className="checkbox" disabled={ mediumDisabled }>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={ this.props.selected.includes(MEDIUM) }
|
|
|
|
disabled={ mediumDisabled }
|
|
|
|
onChange={ this.handleChange(MEDIUM) }/> Medium
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div className="control">
|
|
|
|
<label className="checkbox" disabled={ lowDisabled }>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={ this.props.selected.includes(LOW) }
|
|
|
|
disabled={ lowDisabled }
|
|
|
|
onChange={ this.handleChange(LOW) }/> Low
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var Navigation = React.createClass({
|
|
|
|
updateSeverity: function(vals) {
|
|
|
|
this.props.onSeverity(vals);
|
|
|
|
},
|
|
|
|
updateConfidence: function(vals) {
|
|
|
|
this.props.onConfidence(vals);
|
|
|
|
},
|
|
|
|
updateIssueType: function(e) {
|
|
|
|
if (e.target.value == "all") {
|
|
|
|
this.props.onIssueType(null);
|
|
|
|
} else {
|
|
|
|
this.props.onIssueType(e.target.value);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
var issueTypes = this.props.allIssueTypes
|
|
|
|
.map(function(it) {
|
2018-10-03 19:17:25 +01:00
|
|
|
var matches = this.props.issueType == it
|
2016-10-18 06:36:35 +01:00
|
|
|
return (
|
2018-10-03 19:17:25 +01:00
|
|
|
<option value={ it } selected={ matches }>
|
2016-10-18 06:36:35 +01:00
|
|
|
{ it }
|
|
|
|
</option>
|
|
|
|
);
|
|
|
|
}.bind(this));
|
|
|
|
return (
|
|
|
|
<nav className="panel">
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="panel-heading">Filters</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
<div className="panel-block">
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="field is-horizontal">
|
|
|
|
<div className="field-label is-normal">
|
|
|
|
<label className="label is-pulled-left">Severity</label>
|
|
|
|
</div>
|
|
|
|
<div className="field-body">
|
|
|
|
<LevelSelector selected={ this.props.severity } available={ this.props.allSeverities } onChange={ this.updateSeverity } />
|
|
|
|
</div>
|
|
|
|
</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
<div className="panel-block">
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="field is-horizontal">
|
|
|
|
<div className="field-label is-normal">
|
|
|
|
<label className="label is-pulled-left">Confidence</label>
|
|
|
|
</div>
|
|
|
|
<div className="field-body">
|
|
|
|
<LevelSelector selected={ this.props.confidence } available={ this.props.allConfidences } onChange={ this.updateConfidence } />
|
|
|
|
</div>
|
|
|
|
</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
<div className="panel-block">
|
2021-05-25 09:10:42 +01:00
|
|
|
<div className="field is-horizontal">
|
|
|
|
<div className="field-label is-normal">
|
|
|
|
<label className="label is-pulled-left">Issue type</label>
|
|
|
|
</div>
|
|
|
|
<div className="field-body">
|
|
|
|
<div className="field">
|
|
|
|
<div className="control">
|
|
|
|
<div className="select is-fullwidth">
|
|
|
|
<select onChange={ this.updateIssueType }>
|
|
|
|
<option value="all" selected={ !this.props.issueType }>
|
|
|
|
(all)
|
|
|
|
</option>
|
|
|
|
{ issueTypes }
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-05-21 10:14:43 +01:00
|
|
|
</div>
|
2016-10-18 06:36:35 +01:00
|
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var IssueBrowser = React.createClass({
|
|
|
|
getInitialState: function() {
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
|
|
|
this.updateIssues(this.props.data);
|
|
|
|
},
|
|
|
|
handleSeverity: function(val) {
|
2018-10-03 19:17:25 +01:00
|
|
|
this.updateIssueTypes(this.props.data.Issues, val, this.state.confidence);
|
2016-10-18 06:36:35 +01:00
|
|
|
this.setState({severity: val});
|
|
|
|
},
|
|
|
|
handleConfidence: function(val) {
|
2018-10-03 19:17:25 +01:00
|
|
|
this.updateIssueTypes(this.props.data.Issues, this.state.severity, val);
|
2016-10-18 06:36:35 +01:00
|
|
|
this.setState({confidence: val});
|
|
|
|
},
|
|
|
|
handleIssueType: function(val) {
|
|
|
|
this.setState({issueType: val});
|
|
|
|
},
|
|
|
|
updateIssues: function(data) {
|
|
|
|
if (!data) {
|
|
|
|
this.setState({data: data});
|
|
|
|
return;
|
|
|
|
}
|
2018-10-03 19:17:25 +01:00
|
|
|
var allSeverities = data.Issues
|
2016-10-18 06:36:35 +01:00
|
|
|
.map(function(issue) {
|
|
|
|
return issue.severity
|
|
|
|
})
|
|
|
|
.sort()
|
|
|
|
.filter(function(item, pos, ary) {
|
|
|
|
return !pos || item != ary[pos - 1];
|
|
|
|
});
|
2018-10-03 19:17:25 +01:00
|
|
|
var allConfidences = data.Issues
|
2016-10-18 06:36:35 +01:00
|
|
|
.map(function(issue) {
|
|
|
|
return issue.confidence
|
|
|
|
})
|
|
|
|
.sort()
|
|
|
|
.filter(function(item, pos, ary) {
|
|
|
|
return !pos || item != ary[pos - 1];
|
|
|
|
});
|
|
|
|
var selectedSeverities = allSeverities;
|
|
|
|
var selectedConfidences = allConfidences;
|
2018-10-03 19:17:25 +01:00
|
|
|
this.updateIssueTypes(data.Issues, selectedSeverities, selectedConfidences);
|
2016-10-18 06:36:35 +01:00
|
|
|
this.setState({
|
|
|
|
data: data,
|
|
|
|
severity: selectedSeverities,
|
|
|
|
allSeverities: allSeverities,
|
|
|
|
confidence: selectedConfidences,
|
|
|
|
allConfidences: allConfidences,
|
|
|
|
issueType: null
|
|
|
|
});
|
|
|
|
},
|
|
|
|
updateIssueTypes: function(issues, severities, confidences) {
|
|
|
|
var allTypes = issues
|
|
|
|
.filter(function(issue) {
|
|
|
|
return severities.includes(issue.severity);
|
|
|
|
})
|
|
|
|
.filter(function(issue) {
|
|
|
|
return confidences.includes(issue.confidence);
|
|
|
|
})
|
|
|
|
.map(function(issue) {
|
|
|
|
return issue.details;
|
|
|
|
})
|
|
|
|
.sort()
|
|
|
|
.filter(function(item, pos, ary) {
|
|
|
|
return !pos || item != ary[pos - 1];
|
|
|
|
});
|
|
|
|
if (this.state.issueType && !allTypes.includes(this.state.issueType)) {
|
|
|
|
this.setState({issueType: null});
|
|
|
|
}
|
|
|
|
this.setState({allIssueTypes: allTypes});
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<div className="content">
|
|
|
|
<div className="columns">
|
|
|
|
<div className="column is-one-quarter">
|
|
|
|
<Navigation
|
|
|
|
severity={ this.state.severity }
|
|
|
|
confidence={ this.state.confidence }
|
|
|
|
issueType={ this.state.issueType }
|
|
|
|
allSeverities={ this.state.allSeverities }
|
|
|
|
allConfidences={ this.state.allConfidences }
|
|
|
|
allIssueTypes={ this.state.allIssueTypes }
|
|
|
|
onSeverity={ this.handleSeverity }
|
|
|
|
onConfidence={ this.handleConfidence }
|
|
|
|
onIssueType={ this.handleIssueType }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className="column is-three-quarters">
|
|
|
|
<Issues
|
|
|
|
data={ this.props.data }
|
|
|
|
severity={ this.state.severity }
|
|
|
|
confidence={ this.state.confidence }
|
|
|
|
issueType={ this.state.issueType }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ReactDOM.render(
|
|
|
|
<IssueBrowser data={ data } />,
|
|
|
|
document.getElementById("content")
|
|
|
|
);
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>`
|