2025-08-27 17:18:37 +02:00
|
|
|
package rule
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"go/ast"
|
|
|
|
|
"go/token"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/mgechev/revive/lint"
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-14 18:02:48 +02:00
|
|
|
// UnsecureURLSchemeRule checks if a file contains string literals with unsecure URL schemes.
|
|
|
|
|
// For example: http://... in place of https://....
|
2025-08-27 17:18:37 +02:00
|
|
|
type UnsecureURLSchemeRule struct{}
|
|
|
|
|
|
|
|
|
|
// Apply applied the rule to the given file.
|
|
|
|
|
func (*UnsecureURLSchemeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
|
|
|
|
if file.IsTest() {
|
|
|
|
|
return nil // skip test files
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var failures []lint.Failure
|
|
|
|
|
onFailure := func(failure lint.Failure) {
|
|
|
|
|
failures = append(failures, failure)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w := lintUnsecureURLSchemeRule{
|
|
|
|
|
onFailure: onFailure,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast.Walk(w, file.AST)
|
|
|
|
|
return failures
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Name returns the rule name.
|
|
|
|
|
func (*UnsecureURLSchemeRule) Name() string {
|
|
|
|
|
return "unsecure-url-scheme"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type lintUnsecureURLSchemeRule struct {
|
|
|
|
|
onFailure func(lint.Failure)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const schemeSeparator = "://"
|
|
|
|
|
const schemeHTTP = "http"
|
|
|
|
|
const schemeWS = "ws"
|
|
|
|
|
const urlPrefixHTTP = schemeHTTP + schemeSeparator
|
|
|
|
|
const urlPrefixWS = schemeWS + schemeSeparator
|
|
|
|
|
const lenURLPrefixHTTP = len(urlPrefixHTTP)
|
|
|
|
|
const lenURLPrefixWS = len(urlPrefixWS)
|
|
|
|
|
|
|
|
|
|
func (w lintUnsecureURLSchemeRule) Visit(node ast.Node) ast.Visitor {
|
|
|
|
|
n, ok := node.(*ast.BasicLit)
|
|
|
|
|
if !ok || n.Kind != token.STRING {
|
|
|
|
|
return w // not a string literal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value, _ := strconv.Unquote(n.Value) // n.Value has one of the following forms: "..." or `...`
|
|
|
|
|
|
|
|
|
|
var scheme string
|
|
|
|
|
var lenURLPrefix int
|
|
|
|
|
switch {
|
|
|
|
|
case strings.HasPrefix(value, urlPrefixHTTP):
|
|
|
|
|
scheme = schemeHTTP
|
|
|
|
|
lenURLPrefix = lenURLPrefixHTTP
|
|
|
|
|
case strings.HasPrefix(value, urlPrefixWS):
|
|
|
|
|
scheme = schemeWS
|
|
|
|
|
lenURLPrefix = lenURLPrefixWS
|
|
|
|
|
default:
|
|
|
|
|
return nil // not an URL or not an unsecure one
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(value) <= lenURLPrefix {
|
|
|
|
|
return nil // there is no host part in the string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Contains(value, "localhost") || strings.Contains(value, "127.0.0.1") || strings.Contains(value, "0.0.0.0") || strings.Contains(value, "//::") {
|
|
|
|
|
return nil // do not fail on local URL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.onFailure(lint.Failure{
|
|
|
|
|
Confidence: 1,
|
|
|
|
|
Failure: fmt.Sprintf("prefer secure protocol %s over %s in %s", scheme+"s", scheme, n.Value),
|
|
|
|
|
Node: n,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|