⚠️ Under development!
$ go get -u github.com/picatz/glintLinting rules are defined with a JSON config called a Glintfile (this can be specified at the command-line):
Note: While most JSON parsers don't allow comments (including go),
Glintfiles support non-inline comments useful for light documentation and syntax highlighting hints in different editors.
# -*- mode: json -*-
# vi: set ft=json :
// This comment type is also supported.
{
"rules": [
{
"type": "",
"comment": "",
...
},
...
]
}Note: You get to define your own rules, and perhaps a collection of rules will be made in the future by myself or others. For now, I've created an example in this reposiory in the
Glintfileof this source code. Don't be confused that these are the only checksglintcan perform. This is just an example of the output.
$ glint examples/main.go
examples/main.go:5:2:we don't rely on these packages for almost anything
examples/main.go:10:2:we don't rely on these packages for almost anything
examples/main.go:17:2:we don't use golang.org/x/* packages
examples/main.go:26:32:overly extensive file permissions detected
examples/main.go:67:3:for most structs, ReadTimeout should almost never be 0
examples/main.go:71:3:for most structs, ReadTimeout should almost never be 0
examples/main.go:71:3:always set ReadTimeout to something not 0 when creating an http.Server
examples/main.go:80:58:use 2048 bits or more when generating RSA keys for some reason
examples/main.go:80:29:don't use math.Rand as source of entropy
examples/main.go:82:17:we don't use fmt.Errorf for some silly reaosn
examples/main.go:83:24:we don't use fmt.Errorf for some silly reaosn
examples/main.go:83:35:don't use uppercase error message string in fmt.Errorf formatted errors
examples/main.go:95:2:don't use http.Handle/http.HandleFunc which uses the DefaultServeMux due to possible side-effects
examples/main.go:96:2:don't use http.Handle/http.HandleFunc which uses the DefaultServeMux due to possible side-effects
examples/main.go:101:26:don't use old/weak tls cipher suites in your tls.Config
examples/main.go:106:38:don't use net.Listen to listen on all IPv4 addresses
examples/main.go:106:38:don't use net.Listen to listen on a random portIf the arguments passed to glint are files, then it will parse those. Otherwise, you can pass the string(s) of the package(s) you want to inspect.
$ glint github.com/picatz/doh github.com/picatz/iface github.com/picatz/backdoor
/path/to/src/github.com/picatz/doh/main.go:13:2:we don't use golang.org/x/* packages
/path/to/src/github.com/picatz/backdoor/backdoor.go:11:2:don't use http.Handle/http.HandleFunc which uses the DefaultServeMux due to possible side-effectsNote: Linting messages are output to STDOUT in the following format
file:line:column:comment
All rules require a type and a comment to define what it is and what message is included in the linting error. For each supported rule type, there are a collection of options which help customize how the rule should inspect the program. This attempts to be as declarative as possible.
{
"type": "",
"comment": "",
...
}Using the "import" type you can specify what imported packages should or should not be included in a program.
| Option | Description | Required |
|---|---|---|
match |
list of regular expressions to use to search imports | false |
{
"type": "import",
"comment": "don't use golang.org/x/* packages",
"match": [
"golang.org/x/\\w+"
]
}Using the "method" type you can define certain method calls that should not be used.
| Option | Description | Required |
|---|---|---|
call |
the package.Method call to inspect | false |
call_match |
array of regular expressions to match against a package.Method call to inspect | false |
argument |
the index of the method argument (starting at 0 for the first) to inspect |
false |
less_than |
the method argument, if it's an integer, must be less than the given value | false |
greater_than |
the method argument, if it's an integer, must be greater than the given value | false |
equals |
the method argument, if it's an interger, must equal exactly the given value | false |
avoid |
the method should be avoided no matter what | false |
match |
list of regular expressions to match against method arguments | false |
{
"type": "method",
"comment": "use 2048 bits or more when generating RSA keys for some reason",
"call": "rsa.GenerateKey",
"argument": 1,
"less_than": 2048
}{
"type": "method",
"comment": "we don't use fmt.Errorf for some reaosn",
"call": "fmt.Errorf",
"avoid": true
}{
"type": "method",
"comment": "don't use uppercase error message string in fmt.Errorf formatted errors for some reason",
"call": "fmt.Errorf",
"argument": 0,
"match": [
"^[A-Z]+"
]
}{
"type": "method",
"comment": "don't use math.Rand as source of entropy",
"call_match": [
"rand.New$"
],
"avoid": true
}{
"type": "method",
"comment": "don't use http.Handle/http.HandleFunc which uses the DefaultServeMux due to possible side-effects",
"call_match": [
"http.Handle$",
"http.HandleFunc$"
],
"avoid": true
}{
"type": "method",
"comment": "potential for file inclusion in file name variable, be sure to clean the path if user input",
"call_match": [
"os.Open", "ioutil.ReadFile", "filepath.Join", "path.Join"
],
"avoid": true
}{
"type": "method",
"comment": "overly extensive file permissions detected",
"call_match": [
"os.Mkdir", "os.MkdirAll"
],
"argument": 1,
"greater_than": 750
}Using the "struct" type you can declare rules for structs.
| Option | Description | Required |
|---|---|---|
name |
the name of the package struct to inspect | false |
field |
the specific struct field to inspect | false |
match |
list of regular expressions to match field values | false |
Note: For the
fieldoption, if there are no fields defined when creating the struct in the inspected source code, then the assumed value to check against isnilfor any type. This isn't the "zero value" you might expect, but greatly simplifies the config for checking structs.
{
"type": "struct",
"comment": "don't use TLS 1.3 ciphers in a tls.Config",
"name": "tls.Config",
"field": "CipherSuites",
"match": [
"tls.TLS_AES_128_GCM_SHA256",
"tls.TLS_AES_256_GCM_SHA384",
"tls.TLS_CHACHA20_POLY1305_SHA256"
]
}{
"type": "struct",
"comment": "always set ReadTimeout when creating an http.Server",
"name": "http.Server",
"field": "ReadTimeout",
"match": [
"0", "nil"
]
}{
"type": "struct",
"comment": "http.Server ReadTimeout field should never be a single second",
"name": "http.Server",
"field": "ReadTimeout",
"match": [
"time.Second"
]
}{
"type": "struct",
"comment": "always set ReadTimeout when creating an ExampleServer",
"name": "ExampleServer",
"field": "ReadTimeout",
"match": [
"0", "nil"
]
}Note: The
ExampleServerstruct example is meant to show how to inspect non-imported packaged structs.
type ExampleServer struct {
ReadTimeout time.Duration
// ...
}
// will catch this
var example = ExampleServer{
ReadTimeout: 0,
}
// and this
var example2 = ExampleServer{}{
"type": "struct",
"comment": "for most structs, ReadTimeout should almost never be 0",
"field": "ReadTimeout",
"match": [
"0"
]
}Note: The example above is meant to demonstrate that struct fields of any go struct type can be checked if the
nameoption is ommitted. However, if the struct is created using the implicit zero value (field is not used during initialization), then this check will not apply to it unless the rules matchesnilas well which could accidently be applied to any struct that has ignored fields and should generally be avoided.
// it would be able to check this
ex1 := http.Server{
ReadTimeout: 0,
}
// and this
ex2 := ExampleServer{
ReadTimeout: 0,
}
// but not this
ex2 := ExampleServer{}
// or this this
ex2 := http.Server{}Using the "comment" type you can declare rules for comments within programs.
| Option | Description | Required |
|---|---|---|
match |
a list of regular expressions to search for | false |
{
"type": "comment",
"comment": "don't leave TODO comments without a name i.e. TODO(picat):",
"match": [
"TODO:"
]
}Using the "assginment" type you can declare rules for variable assignments within programs.
| Option | Description | Required |
|---|---|---|
is |
a list of regular expressions match against the variable types | false |
match |
a list of regular expressions match against the variable names | false |
{
"type": "assignment",
"comment": "don't skip error checks",
"is": [ "error" ],
"match": [ "_" ]
}{
"type": "assignment",
"comment": "don't skip boolean checks",
"is": [ "bool" ],
"match": [ "_" ]
}Which will catch things like:
package main
import (
"errors"
)
func okOrErr() (bool, error) {
return false, errors.New("example")
}
func main() {
ok, _ = okOrErr()
if ok {
return
}
}Producing the following error:
$ glint -rules="/path/to/Glintfile" /path/to/main.go
/path/to/main.go:12:6:don't skip error checks