support gas
This commit is contained in:
parent
0af6cacb28
commit
9d95267977
30
Gopkg.lock
generated
30
Gopkg.lock
generated
@ -1,6 +1,16 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/GoASTScanner/gas"
|
||||
packages = [
|
||||
".",
|
||||
"rules"
|
||||
]
|
||||
revision = "a9de4d6c1589158e002cc336c495bf11fbf3ea06"
|
||||
source = "github.com/golangci/gas"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/bradleyfalzon/revgrep"
|
||||
packages = ["."]
|
||||
@ -75,10 +85,10 @@
|
||||
revision = "f557f368b7f3d00a54ed44eb57b9eca59e85cee1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golangci/goconst"
|
||||
packages = ["."]
|
||||
revision = "b67b9035d29a7561902d87eb3757dd490b31c1b1"
|
||||
branch = "master"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -183,6 +193,22 @@
|
||||
revision = "53b9af5a45362a6f2896cfa39cc17d17ba9667ea"
|
||||
source = "github.com/golangci/dupl"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nbutton23/zxcvbn-go"
|
||||
packages = [
|
||||
".",
|
||||
"adjacency",
|
||||
"data",
|
||||
"entropy",
|
||||
"frequency",
|
||||
"match",
|
||||
"matching",
|
||||
"scoring",
|
||||
"utils/math"
|
||||
]
|
||||
revision = "eafdab6b0663b4b528c35975c8b0e78be6e25261"
|
||||
version = "v0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
@ -306,6 +332,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "33466d605f73dcfcecf89a0d3a46aaa719366433cdd6080d00f8476fe77e747f"
|
||||
inputs-digest = "c5c664dfe96bbba11f6249112ed2f5252ec99b832a4a5d70b00ed41c64ac3b6b"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -60,6 +60,11 @@
|
||||
branch = "master"
|
||||
source = "github.com/golangci/interfacer"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/GoASTScanner/gas"
|
||||
branch = "master"
|
||||
source = "github.com/golangci/gas"
|
||||
|
||||
[[override]]
|
||||
name = "mvdan.cc/lint"
|
||||
branch = "master"
|
||||
|
@ -63,6 +63,7 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
||||
enabledByDefault(golinters.Errcheck{}, "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases", true, false),
|
||||
enabledByDefault(golinters.Golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes", false, false),
|
||||
enabledByDefault(golinters.Megacheck{}, "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused", true, true),
|
||||
enabledByDefault(golinters.Gas{}, "Inspects source code for security problems", true, false),
|
||||
enabledByDefault(golinters.Structcheck{}, "Finds unused struct fields", true, false),
|
||||
enabledByDefault(golinters.Varcheck{}, "Finds unused global variables and constants", true, false),
|
||||
enabledByDefault(golinters.Interfacer{}, "Linter that suggests narrower interface types", true, true),
|
||||
|
44
pkg/golinters/gas.go
Normal file
44
pkg/golinters/gas.go
Normal file
@ -0,0 +1,44 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/rules"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
type Gas struct{}
|
||||
|
||||
func (Gas) Name() string {
|
||||
return "gas"
|
||||
}
|
||||
|
||||
func (lint Gas) Run(ctx context.Context, lintCtx *Context) (*result.Result, error) {
|
||||
gasConfig := gas.NewConfig()
|
||||
enabledRules := rules.Generate(rules.NewRuleFilter(true, "G104")) // disable what errcheck does: it reports on Close etc
|
||||
logger := log.New(ioutil.Discard, "", 0)
|
||||
analyzer := gas.NewAnalyzer(gasConfig, logger)
|
||||
analyzer.LoadRules(enabledRules.Builders())
|
||||
|
||||
analyzer.ProcessProgram(lintCtx.Program)
|
||||
issues, _ := analyzer.Report()
|
||||
|
||||
res := &result.Result{}
|
||||
for _, i := range issues {
|
||||
text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
|
||||
line, _ := strconv.Atoi(i.Line)
|
||||
res.Issues = append(res.Issues, result.Issue{
|
||||
File: i.File,
|
||||
LineNumber: line,
|
||||
Text: text,
|
||||
FromLinter: lint.Name(),
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
11
pkg/golinters/testdata/gas.go
vendored
Normal file
11
pkg/golinters/testdata/gas.go
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"crypto/md5" // ERROR "G501: Blacklisted import crypto/md5: weak cryptographic primitive"
|
||||
"log"
|
||||
)
|
||||
|
||||
func Gas() {
|
||||
h := md5.New() // ERROR "G401: Use of weak cryptographic primitive"
|
||||
log.Print(h)
|
||||
}
|
30
vendor/github.com/GoASTScanner/gas/.gitignore
generated
vendored
Normal file
30
vendor/github.com/GoASTScanner/gas/.gitignore
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.swp
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
vendor
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
.DS_Store
|
||||
|
||||
.vscode
|
20
vendor/github.com/GoASTScanner/gas/.travis.yml
generated
vendored
Normal file
20
vendor/github.com/GoASTScanner/gas/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
- "1.10"
|
||||
- tip
|
||||
|
||||
install:
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u github.com/onsi/ginkgo/ginkgo
|
||||
- go get -u github.com/onsi/gomega
|
||||
- go get -u golang.org/x/crypto/ssh
|
||||
- go get -u github.com/GoASTScanner/gas/cmd/gas/...
|
||||
- go get -v -t ./...
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
|
||||
script: make test
|
||||
|
8
vendor/github.com/GoASTScanner/gas/Dockerfile
generated
vendored
Normal file
8
vendor/github.com/GoASTScanner/gas/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
FROM golang:1.9.4-alpine3.7
|
||||
|
||||
ENV BIN=gas
|
||||
|
||||
COPY build/*-linux-amd64 /go/bin/$BIN
|
||||
COPY docker-entrypoint.sh /usr/local/bin
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
134
vendor/github.com/GoASTScanner/gas/Gopkg.lock
generated
vendored
Normal file
134
vendor/github.com/GoASTScanner/gas/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kisielk/gotool"
|
||||
packages = ["."]
|
||||
revision = "0de1eaf82fa3f583ce21fde859f1e7e0c5e9b220"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mozilla/tls-observatory"
|
||||
packages = ["constants"]
|
||||
revision = "8791a200eb40f8625a152bfb8336171305f5f35c"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nbutton23/zxcvbn-go"
|
||||
packages = [
|
||||
".",
|
||||
"adjacency",
|
||||
"data",
|
||||
"entropy",
|
||||
"frequency",
|
||||
"match",
|
||||
"matching",
|
||||
"scoring",
|
||||
"utils/math"
|
||||
]
|
||||
revision = "a22cb81b2ecdde8b68e9ffb8824731cbf88e1de4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/onsi/ginkgo"
|
||||
packages = [
|
||||
".",
|
||||
"config",
|
||||
"internal/codelocation",
|
||||
"internal/containernode",
|
||||
"internal/failer",
|
||||
"internal/leafnodes",
|
||||
"internal/remote",
|
||||
"internal/spec",
|
||||
"internal/spec_iterator",
|
||||
"internal/specrunner",
|
||||
"internal/suite",
|
||||
"internal/testingtproxy",
|
||||
"internal/writer",
|
||||
"reporters",
|
||||
"reporters/stenographer",
|
||||
"reporters/stenographer/support/go-colorable",
|
||||
"reporters/stenographer/support/go-isatty",
|
||||
"types"
|
||||
]
|
||||
revision = "11459a886d9cd66b319dac7ef1e917ee221372c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/onsi/gomega"
|
||||
packages = [
|
||||
".",
|
||||
"format",
|
||||
"internal/assertion",
|
||||
"internal/asyncassertion",
|
||||
"internal/oraclematcher",
|
||||
"internal/testingtsupport",
|
||||
"matchers",
|
||||
"matchers/support/goraph/bipartitegraph",
|
||||
"matchers/support/goraph/edge",
|
||||
"matchers/support/goraph/node",
|
||||
"matchers/support/goraph/util",
|
||||
"types"
|
||||
]
|
||||
revision = "dcabb60a477c2b6f456df65037cb6708210fbb02"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
packages = ["."]
|
||||
revision = "256dc444b735e061061cf46c809487313d5b0065"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"html",
|
||||
"html/atom",
|
||||
"html/charset"
|
||||
]
|
||||
revision = "8351a756f30f1297fe94bbf4b767ec589c6ea6d0"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "164713f0dfcec4e80be8b53e1f0811f5f0d84578"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"encoding",
|
||||
"encoding/charmap",
|
||||
"encoding/htmlindex",
|
||||
"encoding/internal",
|
||||
"encoding/internal/identifier",
|
||||
"encoding/japanese",
|
||||
"encoding/korean",
|
||||
"encoding/simplifiedchinese",
|
||||
"encoding/traditionalchinese",
|
||||
"encoding/unicode",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/utf8internal",
|
||||
"language",
|
||||
"runes",
|
||||
"transform",
|
||||
"unicode/cldr"
|
||||
]
|
||||
revision = "1cbadb444a806fd9430d14ad08967ed91da4fa0a"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/tools"
|
||||
packages = [
|
||||
"go/ast/astutil",
|
||||
"go/buildutil",
|
||||
"go/loader"
|
||||
]
|
||||
revision = "e531a2a1c15f94033f6fa87666caeb19a688175f"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "706e049cd8b8db8705af09e7a375a999d01373a409beadc850c80d64de9849fd"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
38
vendor/github.com/GoASTScanner/gas/Gopkg.toml
generated
vendored
Normal file
38
vendor/github.com/GoASTScanner/gas/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mozilla/tls-observatory"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
154
vendor/github.com/GoASTScanner/gas/LICENSE.txt
generated
vendored
Normal file
154
vendor/github.com/GoASTScanner/gas/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
Apache License
|
||||
|
||||
Version 2.0, January 2004
|
||||
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this
|
||||
License, each Contributor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
|
||||
reproduce, prepare Derivative Works of, publicly display, publicly perform,
|
||||
sublicense, and distribute the Work and such Derivative Works in Source or
|
||||
Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License,
|
||||
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||
license to make, have made, use, offer to sell, sell, import, and otherwise
|
||||
transfer the Work, where such license applies only to those patent claims
|
||||
licensable by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s) with the Work
|
||||
to which such Contribution(s) was submitted. If You institute patent litigation
|
||||
against any entity (including a cross-claim or counterclaim in a lawsuit)
|
||||
alleging that the Work or a Contribution incorporated within the Work
|
||||
constitutes direct or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate as of the date
|
||||
such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||
Derivative Works thereof in any medium, with or without modifications, and in
|
||||
Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and You must retain, in the Source form of
|
||||
any Derivative Works that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works; and If the Work
|
||||
includes a "NOTICE" text file as part of its distribution, then any Derivative
|
||||
Works that You distribute must include a readable copy of the attribution
|
||||
notices contained within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one of the following
|
||||
places: within a NOTICE text file distributed as part of the Derivative Works;
|
||||
within the Source form or documentation, if provided along with the Derivative
|
||||
Works; or, within a display generated by the Derivative Works, if and wherever
|
||||
such third-party notices normally appear. The contents of the NOTICE file are
|
||||
for informational purposes only and do not modify the License. You may add Your
|
||||
own attribution notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided that such
|
||||
additional attribution notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License. 5. Submission of Contributions.
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names,
|
||||
trademarks, service marks, or product names of the Licensor, except as required
|
||||
for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
|
||||
writing, Licensor provides the Work (and each Contributor provides its
|
||||
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied, including, without limitation, any warranties
|
||||
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any risks
|
||||
associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in
|
||||
tort (including negligence), contract, or otherwise, unless required by
|
||||
applicable law (such as deliberate and grossly negligent acts) or agreed to in
|
||||
writing, shall any Contributor be liable to You for damages, including any
|
||||
direct, indirect, special, incidental, or consequential damages of any character
|
||||
arising as a result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill, work stoppage,
|
||||
computer failure or malfunction, or any and all other commercial damages or
|
||||
losses), even if such Contributor has been advised of the possibility of such
|
||||
damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or
|
||||
Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||
acceptance of support, warranty, indemnity, or other liability obligations
|
||||
and/or rights consistent with this License. However, in accepting such
|
||||
obligations, You may act only on Your own behalf and on Your sole
|
||||
responsibility, not on behalf of any other Contributor, and only if You agree to
|
||||
indemnify, defend, and hold each Contributor harmless for any liability incurred
|
||||
by, or claims asserted against, such Contributor by reason of your accepting any
|
||||
such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
48
vendor/github.com/GoASTScanner/gas/Makefile
generated
vendored
Normal file
48
vendor/github.com/GoASTScanner/gas/Makefile
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
GIT_TAG?= $(shell git describe --always --tags)
|
||||
BUILD_DATE = $(shell date +%Y-%m-%d)
|
||||
BIN = gas
|
||||
BUILD_CMD = go build -ldflags "-X main.Version=${VERSION} -X main.GitTag=${GIT_TAG} -X main.BuildDate=${BUILD_DATE}" -o build/$(BIN)-$(VERSION)-$${GOOS}-$${GOARCH} ./cmd/gas/ &
|
||||
FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
|
||||
IMAGE_REPO = docker.io
|
||||
|
||||
default:
|
||||
$(MAKE) bootstrap
|
||||
$(MAKE) build
|
||||
|
||||
test: bootstrap
|
||||
test -z '$(FMT_CMD)'
|
||||
go vet $(go list ./... | grep -v /vendor/)
|
||||
golint -set_exit_status $(shell go list ./... | grep -v vendor)
|
||||
gas ./...
|
||||
ginkgo -r -v
|
||||
bootstrap:
|
||||
dep ensure
|
||||
build:
|
||||
go build -o $(BIN) ./cmd/gas/
|
||||
clean:
|
||||
rm -rf build vendor
|
||||
rm -f release image bootstrap $(BIN)
|
||||
release: bootstrap
|
||||
ifndef VERSION
|
||||
$(error VERSION flag is not set. Run 'make release VERSION=<YOUR VERSION>'.)
|
||||
endif
|
||||
@echo "Running build command..."
|
||||
bash -c '\
|
||||
export GOOS=linux; export GOARCH=amd64; export CGO_ENABLED=0; $(BUILD_CMD) \
|
||||
wait \
|
||||
'
|
||||
touch release
|
||||
|
||||
image: release
|
||||
@echo "Building the Docker image..."
|
||||
docker build -t $(IMAGE_REPO)/$(BIN):$(VERSION) .
|
||||
docker tag $(IMAGE_REPO)/$(BIN):$(VERSION) $(IMAGE_REPO)/$(BIN):latest
|
||||
touch image
|
||||
|
||||
image-push: image
|
||||
@echo "Pushing the Docker image..."
|
||||
docker push $(IMAGE_REPO)/$(BIN):$(VERSION)
|
||||
docker push $(IMAGE_REPO)/$(BIN):latest
|
||||
|
||||
.PHONY: test build clean image-push
|
||||
|
199
vendor/github.com/GoASTScanner/gas/README.md
generated
vendored
Normal file
199
vendor/github.com/GoASTScanner/gas/README.md
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
|
||||
|
||||
## GAS - Go AST Scanner
|
||||
|
||||
Inspects source code for security problems by scanning the Go AST.
|
||||
|
||||
### License
|
||||
|
||||
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 [here](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
### Project status
|
||||
|
||||
[](https://travis-ci.org/GoASTScanner/gas)
|
||||
[](https://godoc.org/github.com/GoASTScanner/gas)
|
||||
|
||||
Gas is still in alpha and accepting feedback from early adopters. We do
|
||||
not consider it production ready at this time.
|
||||
|
||||
### Install
|
||||
|
||||
`$ go get github.com/GoASTScanner/gas/cmd/gas/...`
|
||||
|
||||
### Usage
|
||||
|
||||
Gas can be configured to only run a subset of rules, to exclude certain file
|
||||
paths, and produce reports in different formats. By default all rules will be
|
||||
run against the supplied input files. To recursively scan from the current
|
||||
directory you can supply './...' as the input argument.
|
||||
|
||||
#### Selecting rules
|
||||
|
||||
By default Gas will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the '-include=' flag,
|
||||
or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
|
||||
|
||||
##### Available rules
|
||||
|
||||
- G101: Look for hardcoded credentials
|
||||
- G102: Bind to all interfaces
|
||||
- G103: Audit the use of unsafe block
|
||||
- G104: Audit errors not checked
|
||||
- G105: Audit the use of math/big.Int.Exp
|
||||
- G106: Audit the use of ssh.InsecureIgnoreHostKey
|
||||
- G201: SQL query construction using format string
|
||||
- G202: SQL query construction using string concatenation
|
||||
- G203: Use of unescaped data in HTML templates
|
||||
- G204: Audit use of command execution
|
||||
- G301: Poor file permissions used when creating a directory
|
||||
- G302: Poor file permisions used with chmod
|
||||
- G303: Creating tempfile using a predictable path
|
||||
- G304: File path provided as taint input
|
||||
- G401: Detect the usage of DES, RC4, or MD5
|
||||
- G402: Look for bad TLS connection settings
|
||||
- G403: Ensure minimum RSA key length of 2048 bits
|
||||
- G404: Insecure random number source (rand)
|
||||
- G501: Import blacklist: crypto/md5
|
||||
- G502: Import blacklist: crypto/des
|
||||
- G503: Import blacklist: crypto/rc4
|
||||
- G504: Import blacklist: net/http/cgi
|
||||
|
||||
|
||||
```
|
||||
# Run a specific set of rules
|
||||
$ gas -include=G101,G203,G401 ./...
|
||||
|
||||
# Run everything except for rule G303
|
||||
$ gas -exclude=G303 ./...
|
||||
```
|
||||
|
||||
#### Excluding files:
|
||||
|
||||
Gas will ignore dependencies in your vendor directory any files
|
||||
that are not considered build artifacts by the compiler (so test files).
|
||||
|
||||
#### Annotating code
|
||||
|
||||
As with all automated detection tools there will be cases of false positives. In cases where Gas reports a failure that has been manually verified as being safe it is possible to annotate the code with a '#nosec' comment.
|
||||
|
||||
The annotation causes Gas to stop processing any further nodes within the
|
||||
AST so can apply to a whole block or more granularly to a single expression.
|
||||
|
||||
```go
|
||||
|
||||
import "md5" // #nosec
|
||||
|
||||
|
||||
func main(){
|
||||
|
||||
/* #nosec */
|
||||
if x > y {
|
||||
h := md5.New() // this will also be ignored
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In some cases you may also want to revisit places where #nosec annotations
|
||||
have been used. To run the scanner and ignore any #nosec annotations you
|
||||
can do the following:
|
||||
|
||||
```
|
||||
$ gas -nosec=true ./...
|
||||
```
|
||||
#### Build tags
|
||||
|
||||
Gas is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer.
|
||||
They can be provided as a comma separated list as follows:
|
||||
|
||||
```
|
||||
$ gas -tag debug,ignore ./...
|
||||
```
|
||||
|
||||
### Output formats
|
||||
|
||||
Gas currently supports text, json, yaml, csv and JUnit XML output formats. By default
|
||||
results will be reported to stdout, but can also be written to an output
|
||||
file. The output format is controlled by the '-fmt' flag, and the output file is controlled by the '-out' flag as follows:
|
||||
|
||||
```
|
||||
# Write output in json format to results.json
|
||||
$ gas -fmt=json -out=results.json *.go
|
||||
```
|
||||
### Development
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Install dep according to the instructions here: https://github.com/golang/dep
|
||||
Install the latest version of golint: https://github.com/golang/lint
|
||||
|
||||
#### Build
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
#### Tests
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
#### Release Build
|
||||
|
||||
Gas can be released as follows:
|
||||
|
||||
```bash
|
||||
make release VERSION=2.0.0
|
||||
```
|
||||
|
||||
The released version of the tool is available in the `build` folder. The build information should be displayed in the usage text.
|
||||
|
||||
```
|
||||
./build/gas-2.0.0-linux-amd64 -h
|
||||
|
||||
GAS - Go AST Scanner
|
||||
|
||||
Gas analyzes Go source code to look for common programming mistakes that
|
||||
can lead to security problems.
|
||||
|
||||
VERSION: 2.0.0
|
||||
GIT TAG: 96489ff
|
||||
BUILD DATE: 2018-02-21
|
||||
|
||||
```
|
||||
|
||||
#### Docker image
|
||||
|
||||
You can execute a release and build the docker image as follows:
|
||||
|
||||
```
|
||||
make image VERSION=2.0.0
|
||||
```
|
||||
|
||||
Now you can run the gas tool in a container against your local workspace:
|
||||
|
||||
```
|
||||
docker run -it -v <YOUR LOCAL WORKSPACE>:/workspace gas /workspace
|
||||
```
|
||||
|
||||
#### Generate TLS rule
|
||||
|
||||
The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recommendation](https://statics.tls.security.mozilla.org/server-side-tls-conf.json).
|
||||
|
||||
|
||||
First you need to install the generator tool:
|
||||
|
||||
```
|
||||
go get github.com/GoASTScanner/gas/cmd/tlsconfig/...
|
||||
```
|
||||
|
||||
You can invoke now the `go generate` in the root of the project:
|
||||
|
||||
```
|
||||
go generate ./...
|
||||
```
|
||||
|
||||
This will generate the `rules/tls_config.go` file with will contain the current ciphers recommendation from Mozilla.
|
255
vendor/github.com/GoASTScanner/gas/analyzer.go
generated
vendored
Normal file
255
vendor/github.com/GoASTScanner/gas/analyzer.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
// (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.
|
||||
|
||||
// Modifications copyright (C) 2018 GolangCI
|
||||
|
||||
// Package gas holds the central scanning logic used by GAS
|
||||
package gas
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
// The Context is populated with data parsed from the source code as it is scanned.
|
||||
// It is passed through to all rule functions as they are called. Rules may use
|
||||
// this data in conjunction withe the encoutered AST node.
|
||||
type Context struct {
|
||||
FileSet *token.FileSet
|
||||
Comments ast.CommentMap
|
||||
Info *types.Info
|
||||
Pkg *types.Package
|
||||
Root *ast.File
|
||||
Config map[string]interface{}
|
||||
Imports *ImportTracker
|
||||
Ignores []map[string]bool
|
||||
}
|
||||
|
||||
// Metrics used when reporting information about a scanning run.
|
||||
type Metrics struct {
|
||||
NumFiles int `json:"files"`
|
||||
NumLines int `json:"lines"`
|
||||
NumNosec int `json:"nosec"`
|
||||
NumFound int `json:"found"`
|
||||
}
|
||||
|
||||
// Analyzer object is the main object of GAS. It has methods traverse an AST
|
||||
// and invoke the correct checking rules as on each node as required.
|
||||
type Analyzer struct {
|
||||
ignoreNosec bool
|
||||
ruleset RuleSet
|
||||
context *Context
|
||||
config Config
|
||||
logger *log.Logger
|
||||
issues []*Issue
|
||||
stats *Metrics
|
||||
}
|
||||
|
||||
// NewAnalyzer builds a new anaylzer.
|
||||
func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
|
||||
ignoreNoSec := false
|
||||
if setting, err := conf.GetGlobal("nosec"); err == nil {
|
||||
ignoreNoSec = setting == "true" || setting == "enabled"
|
||||
}
|
||||
if logger == nil {
|
||||
logger = log.New(os.Stderr, "[gas]", log.LstdFlags)
|
||||
}
|
||||
return &Analyzer{
|
||||
ignoreNosec: ignoreNoSec,
|
||||
ruleset: make(RuleSet),
|
||||
context: &Context{},
|
||||
config: conf,
|
||||
logger: logger,
|
||||
issues: make([]*Issue, 0, 16),
|
||||
stats: &Metrics{},
|
||||
}
|
||||
}
|
||||
|
||||
// LoadRules instantiates all the rules to be used when analyzing source
|
||||
// packages
|
||||
func (gas *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
|
||||
for id, def := range ruleDefinitions {
|
||||
r, nodes := def(id, gas.config)
|
||||
gas.ruleset.Register(r, nodes...)
|
||||
}
|
||||
}
|
||||
|
||||
// Process kicks off the analysis process for a given package
|
||||
func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
|
||||
ctx := build.Default
|
||||
ctx.BuildTags = append(ctx.BuildTags, buildTags...)
|
||||
packageConfig := loader.Config{
|
||||
Build: &ctx,
|
||||
ParserMode: parser.ParseComments,
|
||||
AllowErrors: true,
|
||||
}
|
||||
for _, packagePath := range packagePaths {
|
||||
abspath, err := filepath.Abs(packagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(abspath); os.IsNotExist(err) {
|
||||
gas.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
|
||||
continue
|
||||
}
|
||||
gas.logger.Println("Searching directory:", abspath)
|
||||
|
||||
basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var packageFiles []string
|
||||
for _, filename := range basePackage.GoFiles {
|
||||
packageFiles = append(packageFiles, path.Join(packagePath, filename))
|
||||
}
|
||||
|
||||
packageConfig.CreateFromFilenames(basePackage.Name, packageFiles...)
|
||||
}
|
||||
|
||||
builtPackage, err := packageConfig.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gas.ProcessProgram(builtPackage)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessProgram kicks off the analysis process for a given program
|
||||
func (gas *Analyzer) ProcessProgram(builtPackage *loader.Program) {
|
||||
for _, pkg := range builtPackage.InitialPackages() {
|
||||
gas.logger.Println("Checking package:", pkg.String())
|
||||
for _, file := range pkg.Files {
|
||||
gas.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name())
|
||||
gas.context.FileSet = builtPackage.Fset
|
||||
gas.context.Config = gas.config
|
||||
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments)
|
||||
gas.context.Root = file
|
||||
gas.context.Info = &pkg.Info
|
||||
gas.context.Pkg = pkg.Pkg
|
||||
gas.context.Imports = NewImportTracker()
|
||||
gas.context.Imports.TrackPackages(gas.context.Pkg.Imports()...)
|
||||
ast.Walk(gas, file)
|
||||
gas.stats.NumFiles++
|
||||
gas.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ignore a node (and sub-tree) if it is tagged with a "#nosec" comment
|
||||
func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
||||
if groups, ok := gas.context.Comments[n]; ok && !gas.ignoreNosec {
|
||||
for _, group := range groups {
|
||||
if strings.Contains(group.Text(), "#nosec") {
|
||||
gas.stats.NumNosec++
|
||||
|
||||
// Pull out the specific rules that are listed to be ignored.
|
||||
re := regexp.MustCompile("(G\\d{3})")
|
||||
matches := re.FindAllStringSubmatch(group.Text(), -1)
|
||||
|
||||
// If no specific rules were given, ignore everything.
|
||||
if matches == nil || len(matches) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
// Find the rule IDs to ignore.
|
||||
var ignores []string
|
||||
for _, v := range matches {
|
||||
ignores = append(ignores, v[1])
|
||||
}
|
||||
return ignores, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Visit runs the GAS visitor logic over an AST created by parsing go code.
|
||||
// Rule methods added with AddRule will be invoked as necessary.
|
||||
func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
// If we've reached the end of this branch, pop off the ignores stack.
|
||||
if n == nil {
|
||||
if len(gas.context.Ignores) > 0 {
|
||||
gas.context.Ignores = gas.context.Ignores[1:]
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// Get any new rule exclusions.
|
||||
ignoredRules, ignoreAll := gas.ignore(n)
|
||||
if ignoreAll {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now create the union of exclusions.
|
||||
ignores := make(map[string]bool, 0)
|
||||
if len(gas.context.Ignores) > 0 {
|
||||
for k, v := range gas.context.Ignores[0] {
|
||||
ignores[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range ignoredRules {
|
||||
ignores[v] = true
|
||||
}
|
||||
|
||||
// Push the new set onto the stack.
|
||||
gas.context.Ignores = append([]map[string]bool{ignores}, gas.context.Ignores...)
|
||||
|
||||
// Track aliased and initialization imports
|
||||
gas.context.Imports.TrackImport(n)
|
||||
|
||||
for _, rule := range gas.ruleset.RegisteredFor(n) {
|
||||
if _, ok := ignores[rule.ID()]; ok {
|
||||
continue
|
||||
}
|
||||
issue, err := rule.Match(n, gas.context)
|
||||
if err != nil {
|
||||
file, line := GetLocation(n, gas.context)
|
||||
file = path.Base(file)
|
||||
gas.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
|
||||
}
|
||||
if issue != nil {
|
||||
gas.issues = append(gas.issues, issue)
|
||||
gas.stats.NumFound++
|
||||
}
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// Report returns the current issues discovered and the metrics about the scan
|
||||
func (gas *Analyzer) Report() ([]*Issue, *Metrics) {
|
||||
return gas.issues, gas.stats
|
||||
}
|
||||
|
||||
// Reset clears state such as context, issues and metrics from the configured analyzer
|
||||
func (gas *Analyzer) Reset() {
|
||||
gas.context = &Context{}
|
||||
gas.issues = make([]*Issue, 0, 16)
|
||||
gas.stats = &Metrics{}
|
||||
}
|
78
vendor/github.com/GoASTScanner/gas/call_list.go
generated
vendored
Normal file
78
vendor/github.com/GoASTScanner/gas/call_list.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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.
|
||||
|
||||
package gas
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
type set map[string]bool
|
||||
|
||||
// CallList is used to check for usage of specific packages
|
||||
// and functions.
|
||||
type CallList map[string]set
|
||||
|
||||
// NewCallList creates a new empty CallList
|
||||
func NewCallList() CallList {
|
||||
return make(CallList)
|
||||
}
|
||||
|
||||
// AddAll will add several calls to the call list at once
|
||||
func (c CallList) AddAll(selector string, idents ...string) {
|
||||
for _, ident := range idents {
|
||||
c.Add(selector, ident)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a selector and call to the call list
|
||||
func (c CallList) Add(selector, ident string) {
|
||||
if _, ok := c[selector]; !ok {
|
||||
c[selector] = make(set)
|
||||
}
|
||||
c[selector][ident] = true
|
||||
}
|
||||
|
||||
// Contains returns true if the package and function are
|
||||
/// members of this call list.
|
||||
func (c CallList) Contains(selector, ident string) bool {
|
||||
if idents, ok := c[selector]; ok {
|
||||
_, found := idents[ident]
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsCallExpr resolves the call expression name and type
|
||||
/// or package and determines if it exists within the CallList
|
||||
func (c CallList) ContainsCallExpr(n ast.Node, ctx *Context) *ast.CallExpr {
|
||||
selector, ident, err := GetCallInfo(n, ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use only explicit path to reduce conflicts
|
||||
if path, ok := GetImportPath(selector, ctx); ok && c.Contains(path, ident) {
|
||||
return n.(*ast.CallExpr)
|
||||
}
|
||||
|
||||
/*
|
||||
// Try direct resolution
|
||||
if c.Contains(selector, ident) {
|
||||
log.Printf("c.Contains == true, %s, %s.", selector, ident)
|
||||
return n.(*ast.CallExpr)
|
||||
}
|
||||
*/
|
||||
|
||||
return nil
|
||||
}
|
88
vendor/github.com/GoASTScanner/gas/config.go
generated
vendored
Normal file
88
vendor/github.com/GoASTScanner/gas/config.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package gas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// Globals are applicable to all rules and used for general
|
||||
// configuration settings for gas.
|
||||
Globals = "global"
|
||||
)
|
||||
|
||||
// Config is used to provide configuration and customization to each of the rules.
|
||||
type Config map[string]interface{}
|
||||
|
||||
// NewConfig initializes a new configuration instance. The configuration data then
|
||||
// needs to be loaded via c.ReadFrom(strings.NewReader("config data"))
|
||||
// or from a *os.File.
|
||||
func NewConfig() Config {
|
||||
cfg := make(Config)
|
||||
cfg[Globals] = make(map[string]string)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ReadFrom implements the io.ReaderFrom interface. This
|
||||
// should be used with io.Reader to load configuration from
|
||||
//file or from string etc.
|
||||
func (c Config) ReadFrom(r io.Reader) (int64, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return int64(len(data)), err
|
||||
}
|
||||
if err = json.Unmarshal(data, &c); err != nil {
|
||||
return int64(len(data)), err
|
||||
}
|
||||
return int64(len(data)), nil
|
||||
}
|
||||
|
||||
// WriteTo implements the io.WriteTo interface. This should
|
||||
// be used to save or print out the configuration information.
|
||||
func (c Config) WriteTo(w io.Writer) (int64, error) {
|
||||
data, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return int64(len(data)), err
|
||||
}
|
||||
return io.Copy(w, bytes.NewReader(data))
|
||||
}
|
||||
|
||||
// Get returns the configuration section for the supplied key
|
||||
func (c Config) Get(section string) (interface{}, error) {
|
||||
settings, found := c[section]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Section %s not in configuration", section)
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// Set section in the configuration to specified value
|
||||
func (c Config) Set(section string, value interface{}) {
|
||||
c[section] = value
|
||||
}
|
||||
|
||||
// GetGlobal returns value associated with global configuration option
|
||||
func (c Config) GetGlobal(option string) (string, error) {
|
||||
if globals, ok := c[Globals]; ok {
|
||||
if settings, ok := globals.(map[string]string); ok {
|
||||
if value, ok := settings[option]; ok {
|
||||
return value, nil
|
||||
}
|
||||
return "", fmt.Errorf("global setting for %s not found", option)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no global config options found")
|
||||
|
||||
}
|
||||
|
||||
// SetGlobal associates a value with a global configuration ooption
|
||||
func (c Config) SetGlobal(option, value string) {
|
||||
if globals, ok := c[Globals]; ok {
|
||||
if settings, ok := globals.(map[string]string); ok {
|
||||
settings[option] = value
|
||||
}
|
||||
}
|
||||
}
|
2
vendor/github.com/GoASTScanner/gas/docker-entrypoint.sh
generated
vendored
Executable file
2
vendor/github.com/GoASTScanner/gas/docker-entrypoint.sh
generated
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env sh
|
||||
${BIN} "$@"
|
195
vendor/github.com/GoASTScanner/gas/helpers.go
generated
vendored
Normal file
195
vendor/github.com/GoASTScanner/gas/helpers.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
// (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.
|
||||
|
||||
package gas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// MatchCallByPackage ensures that the specified package is imported,
|
||||
// adjusts the name for any aliases and ignores cases that are
|
||||
// initialization only imports.
|
||||
//
|
||||
// Usage:
|
||||
// node, matched := MatchCallByPackage(n, ctx, "math/rand", "Read")
|
||||
//
|
||||
func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*ast.CallExpr, bool) {
|
||||
|
||||
importedName, found := GetImportedName(pkg, c)
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if callExpr, ok := n.(*ast.CallExpr); ok {
|
||||
packageName, callName, err := GetCallInfo(callExpr, c)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if packageName == importedName {
|
||||
for _, name := range names {
|
||||
if callName == name {
|
||||
return callExpr, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// MatchCallByType ensures that the node is a call expression to a
|
||||
// specific object type.
|
||||
//
|
||||
// Usage:
|
||||
// node, matched := MatchCallByType(n, ctx, "bytes.Buffer", "WriteTo", "Write")
|
||||
//
|
||||
func MatchCallByType(n ast.Node, ctx *Context, requiredType string, calls ...string) (*ast.CallExpr, bool) {
|
||||
if callExpr, ok := n.(*ast.CallExpr); ok {
|
||||
typeName, callName, err := GetCallInfo(callExpr, ctx)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if typeName == requiredType {
|
||||
for _, call := range calls {
|
||||
if call == callName {
|
||||
return callExpr, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// MatchCompLit will match an ast.CompositeLit based on the supplied type
|
||||
func MatchCompLit(n ast.Node, ctx *Context, required string) *ast.CompositeLit {
|
||||
if complit, ok := n.(*ast.CompositeLit); ok {
|
||||
typeOf := ctx.Info.TypeOf(complit)
|
||||
if typeOf.String() == required {
|
||||
return complit
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInt will read and return an integer value from an ast.BasicLit
|
||||
func GetInt(n ast.Node) (int64, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.INT {
|
||||
return strconv.ParseInt(node.Value, 0, 64)
|
||||
}
|
||||
return 0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetFloat will read and return a float value from an ast.BasicLit
|
||||
func GetFloat(n ast.Node) (float64, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.FLOAT {
|
||||
return strconv.ParseFloat(node.Value, 64)
|
||||
}
|
||||
return 0.0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetChar will read and return a char value from an ast.BasicLit
|
||||
func GetChar(n ast.Node) (byte, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.CHAR {
|
||||
return node.Value[0], nil
|
||||
}
|
||||
return 0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetString will read and return a string value from an ast.BasicLit
|
||||
func GetString(n ast.Node) (string, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.STRING {
|
||||
return strconv.Unquote(node.Value)
|
||||
}
|
||||
return "", fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetCallObject returns the object and call expression and associated
|
||||
// object for a given AST node. nil, nil will be returned if the
|
||||
// object cannot be resolved.
|
||||
func GetCallObject(n ast.Node, ctx *Context) (*ast.CallExpr, types.Object) {
|
||||
switch node := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
switch fn := node.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
return node, ctx.Info.Uses[fn]
|
||||
case *ast.SelectorExpr:
|
||||
return node, ctx.Info.Uses[fn.Sel]
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetCallInfo returns the package or type and name associated with a
|
||||
// call expression.
|
||||
func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
|
||||
switch node := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
switch fn := node.Fun.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
switch expr := fn.X.(type) {
|
||||
case *ast.Ident:
|
||||
if expr.Obj != nil && expr.Obj.Kind == ast.Var {
|
||||
t := ctx.Info.TypeOf(expr)
|
||||
if t != nil {
|
||||
return t.String(), fn.Sel.Name, nil
|
||||
}
|
||||
return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
|
||||
}
|
||||
return expr.Name, fn.Sel.Name, nil
|
||||
}
|
||||
case *ast.Ident:
|
||||
return ctx.Pkg.Name(), fn.Name, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("unable to determine call info")
|
||||
}
|
||||
|
||||
// GetImportedName returns the name used for the package within the
|
||||
// code. It will resolve aliases and ignores initalization only imports.
|
||||
func GetImportedName(path string, ctx *Context) (string, bool) {
|
||||
importName, imported := ctx.Imports.Imported[path]
|
||||
if !imported {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if _, initonly := ctx.Imports.InitOnly[path]; initonly {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if alias, ok := ctx.Imports.Aliased[path]; ok {
|
||||
importName = alias
|
||||
}
|
||||
return importName, true
|
||||
}
|
||||
|
||||
// GetImportPath resolves the full import path of an identifer based on
|
||||
// the imports in the current context.
|
||||
func GetImportPath(name string, ctx *Context) (string, bool) {
|
||||
for path := range ctx.Imports.Imported {
|
||||
if imported, ok := GetImportedName(path, ctx); ok && imported == name {
|
||||
return path, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// GetLocation returns the filename and line number of an ast.Node
|
||||
func GetLocation(n ast.Node, ctx *Context) (string, int) {
|
||||
fobj := ctx.FileSet.File(n.Pos())
|
||||
return fobj.Name(), fobj.Line(n.Pos())
|
||||
}
|
67
vendor/github.com/GoASTScanner/gas/import_tracker.go
generated
vendored
Normal file
67
vendor/github.com/GoASTScanner/gas/import_tracker.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
|
||||
package gas
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ImportTracker is used to normalize the packages that have been imported
|
||||
// by a source file. It is able to differentiate between plain imports, aliased
|
||||
// imports and init only imports.
|
||||
type ImportTracker struct {
|
||||
Imported map[string]string
|
||||
Aliased map[string]string
|
||||
InitOnly map[string]bool
|
||||
}
|
||||
|
||||
// NewImportTracker creates an empty Import tracker instance
|
||||
func NewImportTracker() *ImportTracker {
|
||||
return &ImportTracker{
|
||||
make(map[string]string),
|
||||
make(map[string]string),
|
||||
make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// TrackPackages tracks all the imports used by the supplied packages
|
||||
func (t *ImportTracker) TrackPackages(pkgs ...*types.Package) {
|
||||
for _, pkg := range pkgs {
|
||||
t.Imported[pkg.Path()] = pkg.Name()
|
||||
// Transient imports
|
||||
//for _, imp := range pkg.Imports() {
|
||||
// t.Imported[imp.Path()] = imp.Name()
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// TrackImport tracks imports and handles the 'unsafe' import
|
||||
func (t *ImportTracker) TrackImport(n ast.Node) {
|
||||
if imported, ok := n.(*ast.ImportSpec); ok {
|
||||
path := strings.Trim(imported.Path.Value, `"`)
|
||||
if imported.Name != nil {
|
||||
if imported.Name.Name == "_" {
|
||||
// Initialization only import
|
||||
t.InitOnly[path] = true
|
||||
} else {
|
||||
// Aliased import
|
||||
t.Aliased[path] = imported.Name.Name
|
||||
}
|
||||
}
|
||||
if path == "unsafe" {
|
||||
t.Imported[path] = path
|
||||
}
|
||||
}
|
||||
}
|
122
vendor/github.com/GoASTScanner/gas/issue.go
generated
vendored
Normal file
122
vendor/github.com/GoASTScanner/gas/issue.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
// (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.
|
||||
|
||||
package gas
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Score type used by severity and confidence values
|
||||
type Score int
|
||||
|
||||
const (
|
||||
// Low severity or confidence
|
||||
Low Score = iota
|
||||
// Medium severity or confidence
|
||||
Medium
|
||||
// High severity or confidence
|
||||
High
|
||||
)
|
||||
|
||||
// Issue is returnd by a GAS rule if it discovers an issue with the scanned code.
|
||||
type Issue struct {
|
||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||
RuleID string `json:"rule_id"` // Human readable explanation
|
||||
What string `json:"details"` // Human readable explanation
|
||||
File string `json:"file"` // File name we found it in
|
||||
Code string `json:"code"` // Impacted code line
|
||||
Line string `json:"line"` // Line number in file
|
||||
}
|
||||
|
||||
// MetaData is embedded in all GAS rules. The Severity, Confidence and What message
|
||||
// will be passed tbhrough to reported issues.
|
||||
type MetaData struct {
|
||||
ID string
|
||||
Severity Score
|
||||
Confidence Score
|
||||
What string
|
||||
}
|
||||
|
||||
// MarshalJSON is used convert a Score object into a JSON representation
|
||||
func (c Score) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(c.String())
|
||||
}
|
||||
|
||||
// String converts a Score into a string
|
||||
func (c Score) String() string {
|
||||
switch c {
|
||||
case High:
|
||||
return "HIGH"
|
||||
case Medium:
|
||||
return "MEDIUM"
|
||||
case Low:
|
||||
return "LOW"
|
||||
}
|
||||
return "UNDEFINED"
|
||||
}
|
||||
|
||||
func codeSnippet(file *os.File, start int64, end int64, n ast.Node) (string, error) {
|
||||
if n == nil {
|
||||
return "", fmt.Errorf("Invalid AST node provided")
|
||||
}
|
||||
|
||||
size := (int)(end - start) // Go bug, os.File.Read should return int64 ...
|
||||
file.Seek(start, 0) // #nosec
|
||||
|
||||
buf := make([]byte, size)
|
||||
if nread, err := file.Read(buf); err != nil || nread != size {
|
||||
return "", fmt.Errorf("Unable to read code")
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// NewIssue creates a new Issue
|
||||
func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score, confidence Score) *Issue {
|
||||
var code string
|
||||
fobj := ctx.FileSet.File(node.Pos())
|
||||
name := fobj.Name()
|
||||
|
||||
start, end := fobj.Line(node.Pos()), fobj.Line(node.End())
|
||||
line := strconv.Itoa(start)
|
||||
if start != end {
|
||||
line = fmt.Sprintf("%d-%d", start, end)
|
||||
}
|
||||
|
||||
// #nosec
|
||||
if file, err := os.Open(fobj.Name()); err == nil {
|
||||
defer file.Close()
|
||||
s := (int64)(fobj.Position(node.Pos()).Offset) // Go bug, should be int64
|
||||
e := (int64)(fobj.Position(node.End()).Offset) // Go bug, should be int64
|
||||
code, err = codeSnippet(file, s, e, node)
|
||||
if err != nil {
|
||||
code = err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return &Issue{
|
||||
File: name,
|
||||
Line: line,
|
||||
RuleID: ruleID,
|
||||
What: desc,
|
||||
Confidence: confidence,
|
||||
Severity: severity,
|
||||
Code: code,
|
||||
}
|
||||
}
|
82
vendor/github.com/GoASTScanner/gas/resolve.go
generated
vendored
Normal file
82
vendor/github.com/GoASTScanner/gas/resolve.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
// (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.
|
||||
|
||||
package gas
|
||||
|
||||
import "go/ast"
|
||||
|
||||
func resolveIdent(n *ast.Ident, c *Context) bool {
|
||||
|
||||
if n.Obj == nil || n.Obj.Kind != ast.Var {
|
||||
return true
|
||||
}
|
||||
if node, ok := n.Obj.Decl.(ast.Node); ok {
|
||||
return TryResolve(node, c)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func resolveAssign(n *ast.AssignStmt, c *Context) bool {
|
||||
for _, arg := range n.Rhs {
|
||||
if !TryResolve(arg, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func resolveCompLit(n *ast.CompositeLit, c *Context) bool {
|
||||
for _, arg := range n.Elts {
|
||||
if !TryResolve(arg, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func resolveBinExpr(n *ast.BinaryExpr, c *Context) bool {
|
||||
return (TryResolve(n.X, c) && TryResolve(n.Y, c))
|
||||
}
|
||||
|
||||
func resolveCallExpr(n *ast.CallExpr, c *Context) bool {
|
||||
// TODO(tkelsey): next step, full function resolution
|
||||
return false
|
||||
}
|
||||
|
||||
// TryResolve will attempt, given a subtree starting at some ATS node, to resolve
|
||||
// all values contained within to a known constant. It is used to check for any
|
||||
// unkown values in compound expressions.
|
||||
func TryResolve(n ast.Node, c *Context) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.BasicLit:
|
||||
return true
|
||||
|
||||
case *ast.CompositeLit:
|
||||
return resolveCompLit(node, c)
|
||||
|
||||
case *ast.Ident:
|
||||
return resolveIdent(node, c)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
return resolveAssign(node, c)
|
||||
|
||||
case *ast.CallExpr:
|
||||
return resolveCallExpr(node, c)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
return resolveBinExpr(node, c)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
59
vendor/github.com/GoASTScanner/gas/rule.go
generated
vendored
Normal file
59
vendor/github.com/GoASTScanner/gas/rule.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
package gas
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// The Rule interface used by all rules supported by GAS.
|
||||
type Rule interface {
|
||||
ID() string
|
||||
Match(ast.Node, *Context) (*Issue, error)
|
||||
}
|
||||
|
||||
// RuleBuilder is used to register a rule definition with the analyzer
|
||||
type RuleBuilder func(id string, c Config) (Rule, []ast.Node)
|
||||
|
||||
// A RuleSet maps lists of rules to the type of AST node they should be run on.
|
||||
// The anaylzer will only invoke rules contained in the list associated with the
|
||||
// type of AST node it is currently visiting.
|
||||
type RuleSet map[reflect.Type][]Rule
|
||||
|
||||
// NewRuleSet constructs a new RuleSet
|
||||
func NewRuleSet() RuleSet {
|
||||
return make(RuleSet)
|
||||
}
|
||||
|
||||
// Register adds a trigger for the supplied rule for the the
|
||||
// specified ast nodes.
|
||||
func (r RuleSet) Register(rule Rule, nodes ...ast.Node) {
|
||||
for _, n := range nodes {
|
||||
t := reflect.TypeOf(n)
|
||||
if rules, ok := r[t]; ok {
|
||||
r[t] = append(rules, rule)
|
||||
} else {
|
||||
r[t] = []Rule{rule}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RegisteredFor will return all rules that are registered for a
|
||||
// specified ast node.
|
||||
func (r RuleSet) RegisteredFor(n ast.Node) []Rule {
|
||||
if rules, found := r[reflect.TypeOf(n)]; found {
|
||||
return rules
|
||||
}
|
||||
return []Rule{}
|
||||
}
|
52
vendor/github.com/GoASTScanner/gas/rules/big.go
generated
vendored
Normal file
52
vendor/github.com/GoASTScanner/gas/rules/big.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type usingBigExp struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *usingBigExp) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matched := gas.MatchCallByType(n, c, r.pkg, r.calls...); matched {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsingBigExp detects issues with modulus == 0 for Bignum
|
||||
func NewUsingBigExp(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &usingBigExp{
|
||||
pkg: "*math/big.Int",
|
||||
calls: []string{"Exp"},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
What: "Use of math/big.Int.Exp function should be audited for modulus == 0",
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
64
vendor/github.com/GoASTScanner/gas/rules/bind.go
generated
vendored
Normal file
64
vendor/github.com/GoASTScanner/gas/rules/bind.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
|
||||
type bindsToAllNetworkInterfaces struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
pattern *regexp.Regexp
|
||||
}
|
||||
|
||||
func (r *bindsToAllNetworkInterfaces) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
callExpr := r.calls.ContainsCallExpr(n, c)
|
||||
if callExpr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if arg, err := gas.GetString(callExpr.Args[1]); err == nil {
|
||||
if r.pattern.MatchString(arg) {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewBindsToAllNetworkInterfaces detects socket connections that are setup to
|
||||
// listen on all network interfaces.
|
||||
func NewBindsToAllNetworkInterfaces(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("net", "Listen")
|
||||
calls.Add("crypto/tls", "Listen")
|
||||
return &bindsToAllNetworkInterfaces{
|
||||
calls: calls,
|
||||
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "Binds to all network interfaces",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
87
vendor/github.com/GoASTScanner/gas/rules/blacklist.go
generated
vendored
Normal file
87
vendor/github.com/GoASTScanner/gas/rules/blacklist.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type blacklistedImport struct {
|
||||
gas.MetaData
|
||||
Blacklisted map[string]string
|
||||
}
|
||||
|
||||
func unquote(original string) string {
|
||||
copy := strings.TrimSpace(original)
|
||||
copy = strings.TrimLeft(copy, `"`)
|
||||
return strings.TrimRight(copy, `"`)
|
||||
}
|
||||
|
||||
func (r *blacklistedImport) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node, ok := n.(*ast.ImportSpec); ok {
|
||||
if description, ok := r.Blacklisted[unquote(node.Path.Value)]; ok {
|
||||
return gas.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewBlacklistedImports reports when a blacklisted import is being used.
|
||||
// Typically when a deprecated technology is being used.
|
||||
func NewBlacklistedImports(id string, conf gas.Config, blacklist map[string]string) (gas.Rule, []ast.Node) {
|
||||
return &blacklistedImport{
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
Blacklisted: blacklist,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
}
|
||||
|
||||
// NewBlacklistedImportMD5 fails if MD5 is imported
|
||||
func NewBlacklistedImportMD5(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportDES fails if DES is imported
|
||||
func NewBlacklistedImportDES(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportRC4 fails if DES is imported
|
||||
func NewBlacklistedImportRC4(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportCGI fails if CGI is imported
|
||||
func NewBlacklistedImportCGI(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
|
||||
})
|
||||
}
|
102
vendor/github.com/GoASTScanner/gas/rules/errors.go
generated
vendored
Normal file
102
vendor/github.com/GoASTScanner/gas/rules/errors.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type noErrorCheck struct {
|
||||
gas.MetaData
|
||||
whitelist gas.CallList
|
||||
}
|
||||
|
||||
func (r *noErrorCheck) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
|
||||
if tv := ctx.Info.TypeOf(callExpr); tv != nil {
|
||||
switch t := tv.(type) {
|
||||
case *types.Tuple:
|
||||
for pos := 0; pos < t.Len(); pos++ {
|
||||
variable := t.At(pos)
|
||||
if variable != nil && variable.Type().String() == "error" {
|
||||
return pos
|
||||
}
|
||||
}
|
||||
case *types.Named:
|
||||
if t.String() == "error" {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
switch stmt := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range stmt.Rhs {
|
||||
if callExpr, ok := expr.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(expr, ctx) == nil {
|
||||
pos := returnsError(callExpr, ctx)
|
||||
if pos < 0 || pos >= len(stmt.Lhs) {
|
||||
return nil, nil
|
||||
}
|
||||
if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
|
||||
return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.ExprStmt:
|
||||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
|
||||
pos := returnsError(callExpr, ctx)
|
||||
if pos >= 0 {
|
||||
return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewNoErrorCheck detects if the returned error is unchecked
|
||||
func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
// TODO(gm) Come up with sensible defaults here. Or flip it to use a
|
||||
// black list instead.
|
||||
whitelist := gas.NewCallList()
|
||||
whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString")
|
||||
whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln")
|
||||
whitelist.Add("io.PipeWriter", "CloseWithError")
|
||||
|
||||
if configured, ok := conf["G104"]; ok {
|
||||
if whitelisted, ok := configured.(map[string][]string); ok {
|
||||
for key, val := range whitelisted {
|
||||
whitelist.AddAll(key, val...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &noErrorCheck{
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
What: "Errors unhandled.",
|
||||
},
|
||||
whitelist: whitelist,
|
||||
}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
|
||||
}
|
95
vendor/github.com/GoASTScanner/gas/rules/fileperms.go
generated
vendored
Normal file
95
vendor/github.com/GoASTScanner/gas/rules/fileperms.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type filePermissions struct {
|
||||
gas.MetaData
|
||||
mode int64
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *filePermissions) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMode int64) int64 {
|
||||
var mode = defaultMode
|
||||
if value, ok := conf[configKey]; ok {
|
||||
switch value.(type) {
|
||||
case int64:
|
||||
mode = value.(int64)
|
||||
case string:
|
||||
if m, e := strconv.ParseInt(value.(string), 0, 64); e != nil {
|
||||
mode = defaultMode
|
||||
} else {
|
||||
mode = m
|
||||
}
|
||||
}
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if callexpr, matched := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
|
||||
modeArg := callexpr.Args[len(callexpr.Args)-1]
|
||||
if mode, err := gas.GetInt(modeArg); err == nil && mode > r.mode {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewFilePerms creates a rule to detect file creation with a more permissive than configured
|
||||
// permission mask.
|
||||
func NewFilePerms(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G302", 0600)
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"OpenFile", "Chmod"},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: fmt.Sprintf("Expect file permissions to be %#o or less", mode),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
||||
// NewMkdirPerms creates a rule to detect directory creation with more permissive than
|
||||
// configured permission mask.
|
||||
func NewMkdirPerms(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G301", 0750)
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"Mkdir", "MkdirAll"},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
147
vendor/github.com/GoASTScanner/gas/rules/hardcoded_credentials.go
generated
vendored
Normal file
147
vendor/github.com/GoASTScanner/gas/rules/hardcoded_credentials.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/nbutton23/zxcvbn-go"
|
||||
)
|
||||
|
||||
type credentials struct {
|
||||
gas.MetaData
|
||||
pattern *regexp.Regexp
|
||||
entropyThreshold float64
|
||||
perCharThreshold float64
|
||||
truncate int
|
||||
ignoreEntropy bool
|
||||
}
|
||||
|
||||
func (r *credentials) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func truncate(s string, n int) string {
|
||||
if n > len(s) {
|
||||
return s
|
||||
}
|
||||
return s[:n]
|
||||
}
|
||||
|
||||
func (r *credentials) isHighEntropyString(str string) bool {
|
||||
s := truncate(str, r.truncate)
|
||||
info := zxcvbn.PasswordStrength(s, []string{})
|
||||
entropyPerChar := info.Entropy / float64(len(s))
|
||||
return (info.Entropy >= r.entropyThreshold ||
|
||||
(info.Entropy >= (r.entropyThreshold/2) &&
|
||||
entropyPerChar >= r.perCharThreshold))
|
||||
}
|
||||
|
||||
func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
return r.matchAssign(node, ctx)
|
||||
case *ast.ValueSpec:
|
||||
return r.matchValueSpec(node, ctx)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*gas.Issue, error) {
|
||||
for _, i := range assign.Lhs {
|
||||
if ident, ok := i.(*ast.Ident); ok {
|
||||
if r.pattern.MatchString(ident.Name) {
|
||||
for _, e := range assign.Rhs {
|
||||
if val, err := gas.GetString(e); err == nil {
|
||||
if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
|
||||
return gas.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gas.Context) (*gas.Issue, error) {
|
||||
for index, ident := range valueSpec.Names {
|
||||
if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil {
|
||||
// const foo, bar = "same value"
|
||||
if len(valueSpec.Values) <= index {
|
||||
index = len(valueSpec.Values) - 1
|
||||
}
|
||||
if val, err := gas.GetString(valueSpec.Values[index]); err == nil {
|
||||
if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
|
||||
return gas.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewHardcodedCredentials attempts to find high entropy string constants being
|
||||
// assigned to variables that appear to be related to credentials.
|
||||
func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
pattern := `(?i)passwd|pass|password|pwd|secret|token`
|
||||
entropyThreshold := 80.0
|
||||
perCharThreshold := 3.0
|
||||
ignoreEntropy := false
|
||||
var truncateString = 16
|
||||
if val, ok := conf["G101"]; ok {
|
||||
conf := val.(map[string]string)
|
||||
if configPattern, ok := conf["pattern"]; ok {
|
||||
pattern = configPattern
|
||||
}
|
||||
if configIgnoreEntropy, ok := conf["ignore_entropy"]; ok {
|
||||
if parsedBool, err := strconv.ParseBool(configIgnoreEntropy); err == nil {
|
||||
ignoreEntropy = parsedBool
|
||||
}
|
||||
}
|
||||
if configEntropyThreshold, ok := conf["entropy_threshold"]; ok {
|
||||
if parsedNum, err := strconv.ParseFloat(configEntropyThreshold, 64); err == nil {
|
||||
entropyThreshold = parsedNum
|
||||
}
|
||||
}
|
||||
if configCharThreshold, ok := conf["per_char_threshold"]; ok {
|
||||
if parsedNum, err := strconv.ParseFloat(configCharThreshold, 64); err == nil {
|
||||
perCharThreshold = parsedNum
|
||||
}
|
||||
}
|
||||
if configTruncate, ok := conf["truncate"]; ok {
|
||||
if parsedInt, err := strconv.Atoi(configTruncate); err == nil {
|
||||
truncateString = parsedInt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &credentials{
|
||||
pattern: regexp.MustCompile(pattern),
|
||||
entropyThreshold: entropyThreshold,
|
||||
perCharThreshold: perCharThreshold,
|
||||
ignoreEntropy: ignoreEntropy,
|
||||
truncate: truncateString,
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
What: "Potential hardcoded credentials",
|
||||
Confidence: gas.Low,
|
||||
Severity: gas.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil)}
|
||||
}
|
55
vendor/github.com/GoASTScanner/gas/rules/rand.go
generated
vendored
Normal file
55
vendor/github.com/GoASTScanner/gas/rules/rand.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type weakRand struct {
|
||||
gas.MetaData
|
||||
funcNames []string
|
||||
packagePath string
|
||||
}
|
||||
|
||||
func (w *weakRand) ID() string {
|
||||
return w.MetaData.ID
|
||||
}
|
||||
|
||||
func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
for _, funcName := range w.funcNames {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
|
||||
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
|
||||
func NewWeakRandCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &weakRand{
|
||||
funcNames: []string{"Read", "Int"},
|
||||
packagePath: "math/rand",
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.High,
|
||||
Confidence: gas.Medium,
|
||||
What: "Use of weak random number generator (math/rand instead of crypto/rand)",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
63
vendor/github.com/GoASTScanner/gas/rules/readfile.go
generated
vendored
Normal file
63
vendor/github.com/GoASTScanner/gas/rules/readfile.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type readfile struct {
|
||||
gas.MetaData
|
||||
gas.CallList
|
||||
}
|
||||
|
||||
// ID returns the identifier for this rule
|
||||
func (r *readfile) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
// Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile`
|
||||
func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := r.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewReadFile detects cases where we read files
|
||||
func NewReadFile(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &readfile{
|
||||
CallList: gas.NewCallList(),
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
What: "Potential file inclusion via variable",
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
}
|
||||
rule.Add("io/ioutil", "ReadFile")
|
||||
rule.Add("os", "Open")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
58
vendor/github.com/GoASTScanner/gas/rules/rsa.go
generated
vendored
Normal file
58
vendor/github.com/GoASTScanner/gas/rules/rsa.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type weakKeyStrength struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
bits int
|
||||
}
|
||||
|
||||
func (w *weakKeyStrength) ID() string {
|
||||
return w.MetaData.ID
|
||||
}
|
||||
|
||||
func (w *weakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if callExpr := w.calls.ContainsCallExpr(n, c); callExpr != nil {
|
||||
if bits, err := gas.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
|
||||
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits
|
||||
func NewWeakKeyStrength(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("crypto/rsa", "GenerateKey")
|
||||
bits := 2048
|
||||
return &weakKeyStrength{
|
||||
calls: calls,
|
||||
bits: bits,
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: fmt.Sprintf("RSA keys should be at least %d bits", bits),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
108
vendor/github.com/GoASTScanner/gas/rules/rulelist.go
generated
vendored
Normal file
108
vendor/github.com/GoASTScanner/gas/rules/rulelist.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// RuleDefinition contains the description of a rule and a mechanism to
|
||||
// create it.
|
||||
type RuleDefinition struct {
|
||||
ID string
|
||||
Description string
|
||||
Create gas.RuleBuilder
|
||||
}
|
||||
|
||||
// RuleList is a mapping of rule ID's to rule definitions
|
||||
type RuleList map[string]RuleDefinition
|
||||
|
||||
// Builders returns all the create methods for a given rule list
|
||||
func (rl RuleList) Builders() map[string]gas.RuleBuilder {
|
||||
builders := make(map[string]gas.RuleBuilder)
|
||||
for _, def := range rl {
|
||||
builders[def.ID] = def.Create
|
||||
}
|
||||
return builders
|
||||
}
|
||||
|
||||
// RuleFilter can be used to include or exclude a rule depending on the return
|
||||
// value of the function
|
||||
type RuleFilter func(string) bool
|
||||
|
||||
// NewRuleFilter is a closure that will include/exclude the rule ID's based on
|
||||
// the supplied boolean value.
|
||||
func NewRuleFilter(action bool, ruleIDs ...string) RuleFilter {
|
||||
rulelist := make(map[string]bool)
|
||||
for _, rule := range ruleIDs {
|
||||
rulelist[rule] = true
|
||||
}
|
||||
return func(rule string) bool {
|
||||
if _, found := rulelist[rule]; found {
|
||||
return action
|
||||
}
|
||||
return !action
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the list of rules to use
|
||||
func Generate(filters ...RuleFilter) RuleList {
|
||||
rules := []RuleDefinition{
|
||||
// misc
|
||||
{"G101", "Look for hardcoded credentials", NewHardcodedCredentials},
|
||||
{"G102", "Bind to all interfaces", NewBindsToAllNetworkInterfaces},
|
||||
{"G103", "Audit the use of unsafe block", NewUsingUnsafe},
|
||||
{"G104", "Audit errors not checked", NewNoErrorCheck},
|
||||
{"G105", "Audit the use of big.Exp function", NewUsingBigExp},
|
||||
{"G106", "Audit the use of ssh.InsecureIgnoreHostKey function", NewSSHHostKey},
|
||||
|
||||
// injection
|
||||
{"G201", "SQL query construction using format string", NewSQLStrFormat},
|
||||
{"G202", "SQL query construction using string concatenation", NewSQLStrConcat},
|
||||
{"G203", "Use of unescaped data in HTML templates", NewTemplateCheck},
|
||||
{"G204", "Audit use of command execution", NewSubproc},
|
||||
|
||||
// filesystem
|
||||
{"G301", "Poor file permissions used when creating a directory", NewMkdirPerms},
|
||||
{"G302", "Poor file permisions used when creation file or using chmod", NewFilePerms},
|
||||
{"G303", "Creating tempfile using a predictable path", NewBadTempFile},
|
||||
{"G304", "File path provided as taint input", NewReadFile},
|
||||
|
||||
// crypto
|
||||
{"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography},
|
||||
{"G402", "Look for bad TLS connection settings", NewIntermediateTLSCheck},
|
||||
{"G403", "Ensure minimum RSA key length of 2048 bits", NewWeakKeyStrength},
|
||||
{"G404", "Insecure random number source (rand)", NewWeakRandCheck},
|
||||
|
||||
// blacklist
|
||||
{"G501", "Import blacklist: crypto/md5", NewBlacklistedImportMD5},
|
||||
{"G502", "Import blacklist: crypto/des", NewBlacklistedImportDES},
|
||||
{"G503", "Import blacklist: crypto/rc4", NewBlacklistedImportRC4},
|
||||
{"G504", "Import blacklist: net/http/cgi", NewBlacklistedImportCGI},
|
||||
}
|
||||
|
||||
ruleMap := make(map[string]RuleDefinition)
|
||||
|
||||
RULES:
|
||||
for _, rule := range rules {
|
||||
for _, filter := range filters {
|
||||
if filter(rule.ID) {
|
||||
continue RULES
|
||||
}
|
||||
}
|
||||
ruleMap[rule.ID] = rule
|
||||
}
|
||||
return ruleMap
|
||||
}
|
135
vendor/github.com/GoASTScanner/gas/rules/sql.go
generated
vendored
Normal file
135
vendor/github.com/GoASTScanner/gas/rules/sql.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type sqlStatement struct {
|
||||
gas.MetaData
|
||||
|
||||
// Contains a list of patterns which must all match for the rule to match.
|
||||
patterns []*regexp.Regexp
|
||||
}
|
||||
|
||||
func (s *sqlStatement) ID() string {
|
||||
return s.MetaData.ID
|
||||
}
|
||||
|
||||
// See if the string matches the patterns for the statement.
|
||||
func (s *sqlStatement) MatchPatterns(str string) bool {
|
||||
for _, pattern := range s.patterns {
|
||||
if !pattern.MatchString(str) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type sqlStrConcat struct {
|
||||
sqlStatement
|
||||
}
|
||||
|
||||
func (s *sqlStrConcat) ID() string {
|
||||
return s.MetaData.ID
|
||||
}
|
||||
|
||||
// see if we can figure out what it is
|
||||
func (s *sqlStrConcat) checkObject(n *ast.Ident) bool {
|
||||
if n.Obj != nil {
|
||||
return n.Obj.Kind != ast.Var && n.Obj.Kind != ast.Fun
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1"
|
||||
func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||
if start, ok := node.X.(*ast.BasicLit); ok {
|
||||
if str, e := gas.GetString(start); e == nil {
|
||||
if !s.MatchPatterns(str) {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := node.Y.(*ast.BasicLit); ok {
|
||||
return nil, nil // string cat OK
|
||||
}
|
||||
if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second) {
|
||||
return nil, nil
|
||||
}
|
||||
return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSQLStrConcat looks for cases where we are building SQL strings via concatenation
|
||||
func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &sqlStrConcat{
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "SQL string concatenation",
|
||||
},
|
||||
},
|
||||
}, []ast.Node{(*ast.BinaryExpr)(nil)}
|
||||
}
|
||||
|
||||
type sqlStrFormat struct {
|
||||
sqlStatement
|
||||
calls gas.CallList
|
||||
}
|
||||
|
||||
// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)"
|
||||
func (s *sqlStrFormat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
|
||||
// TODO(gm) improve confidence if database/sql is being used
|
||||
if node := s.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
|
||||
func NewSQLStrFormat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &sqlStrFormat{
|
||||
calls: gas.NewCallList(),
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
|
||||
regexp.MustCompile("%[^bdoxXfFp]"),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "SQL string formatting",
|
||||
},
|
||||
},
|
||||
}
|
||||
rule.calls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
38
vendor/github.com/GoASTScanner/gas/rules/ssh.go
generated
vendored
Normal file
38
vendor/github.com/GoASTScanner/gas/rules/ssh.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type sshHostKey struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *sshHostKey) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *sshHostKey) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback.
|
||||
func NewSSHHostKey(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &sshHostKey{
|
||||
pkg: "golang.org/x/crypto/ssh",
|
||||
calls: []string{"InsecureIgnoreHostKey"},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
What: "Use of ssh InsecureIgnoreHostKey should be audited",
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
63
vendor/github.com/GoASTScanner/gas/rules/subproc.go
generated
vendored
Normal file
63
vendor/github.com/GoASTScanner/gas/rules/subproc.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type subprocess struct {
|
||||
gas.MetaData
|
||||
gas.CallList
|
||||
}
|
||||
|
||||
func (r *subprocess) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
// TODO(gm) The only real potential for command injection with a Go project
|
||||
// is something like this:
|
||||
//
|
||||
// syscall.Exec("/bin/sh", []string{"-c", tainted})
|
||||
//
|
||||
// E.g. Input is correctly escaped but the execution context being used
|
||||
// is unsafe. For example:
|
||||
//
|
||||
// syscall.Exec("echo", "foobar" + tainted)
|
||||
func (r *subprocess) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := r.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) {
|
||||
return gas.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gas.Medium, gas.High), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas.NewIssue(c, n, r.ID(), "Subprocess launching should be audited", gas.Low, gas.High), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSubproc detects cases where we are forking out to an external process
|
||||
func NewSubproc(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &subprocess{gas.MetaData{ID: id}, gas.NewCallList()}
|
||||
rule.Add("os/exec", "Command")
|
||||
rule.Add("syscall", "Exec")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
58
vendor/github.com/GoASTScanner/gas/rules/tempfiles.go
generated
vendored
Normal file
58
vendor/github.com/GoASTScanner/gas/rules/tempfiles.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type badTempFile struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
args *regexp.Regexp
|
||||
}
|
||||
|
||||
func (t *badTempFile) ID() string {
|
||||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func (t *badTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewBadTempFile detects direct writes to predictable path in temporary directory
|
||||
func NewBadTempFile(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("io/ioutil", "WriteFile")
|
||||
calls.Add("os", "Create")
|
||||
return &badTempFile{
|
||||
calls: calls,
|
||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "File creation in shared tmp directory without using ioutil.Tempfile",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
61
vendor/github.com/GoASTScanner/gas/rules/templates.go
generated
vendored
Normal file
61
vendor/github.com/GoASTScanner/gas/rules/templates.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type templateCheck struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
}
|
||||
|
||||
func (t *templateCheck) ID() string {
|
||||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe
|
||||
return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewTemplateCheck constructs the template check rule. This rule is used to
|
||||
// find use of tempaltes where HTML/JS escaping is not being used
|
||||
func NewTemplateCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("html/template", "HTML")
|
||||
calls.Add("html/template", "HTMLAttr")
|
||||
calls.Add("html/template", "JS")
|
||||
calls.Add("html/template", "URL")
|
||||
return &templateCheck{
|
||||
calls: calls,
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.Low,
|
||||
What: "this method will not auto-escape HTML. Verify data is well formed.",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
130
vendor/github.com/GoASTScanner/gas/rules/tls.go
generated
vendored
Normal file
130
vendor/github.com/GoASTScanner/gas/rules/tls.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
// (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.
|
||||
|
||||
//go:generate tlsconfig
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type insecureConfigTLS struct {
|
||||
gas.MetaData
|
||||
MinVersion int16
|
||||
MaxVersion int16
|
||||
requiredType string
|
||||
goodCiphers []string
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) ID() string {
|
||||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) *gas.Issue {
|
||||
|
||||
if ciphers, ok := n.(*ast.CompositeLit); ok {
|
||||
for _, cipher := range ciphers.Elts {
|
||||
if ident, ok := cipher.(*ast.SelectorExpr); ok {
|
||||
if !stringInSlice(ident.Sel.Name, t.goodCiphers) {
|
||||
err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name)
|
||||
return gas.NewIssue(c, ident, t.ID(), err, gas.High, gas.High)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Context) *gas.Issue {
|
||||
if ident, ok := n.Key.(*ast.Ident); ok {
|
||||
switch ident.Name {
|
||||
|
||||
case "InsecureSkipVerify":
|
||||
if node, ok := n.Value.(*ast.Ident); ok {
|
||||
if node.Name != "false" {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gas.High, gas.High)
|
||||
}
|
||||
} else {
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gas.High, gas.Low)
|
||||
}
|
||||
|
||||
case "PreferServerCipherSuites":
|
||||
if node, ok := n.Value.(*ast.Ident); ok {
|
||||
if node.Name == "false" {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gas.Medium, gas.High)
|
||||
}
|
||||
} else {
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gas.Medium, gas.Low)
|
||||
}
|
||||
|
||||
case "MinVersion":
|
||||
if ival, ierr := gas.GetInt(n.Value); ierr == nil {
|
||||
if (int16)(ival) < t.MinVersion {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gas.High, gas.High)
|
||||
}
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gas.High, gas.Low)
|
||||
}
|
||||
|
||||
case "MaxVersion":
|
||||
if ival, ierr := gas.GetInt(n.Value); ierr == nil {
|
||||
if (int16)(ival) < t.MaxVersion {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gas.High, gas.High)
|
||||
}
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gas.High, gas.Low)
|
||||
}
|
||||
|
||||
case "CipherSuites":
|
||||
if ret := t.processTLSCipherSuites(n.Value, c); ret != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil {
|
||||
actualType := c.Info.TypeOf(complit.Type)
|
||||
if actualType != nil && actualType.String() == t.requiredType {
|
||||
for _, elt := range complit.Elts {
|
||||
if kve, ok := elt.(*ast.KeyValueExpr); ok {
|
||||
issue := t.processTLSConfVal(kve, c)
|
||||
if issue != nil {
|
||||
return issue, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
141
vendor/github.com/GoASTScanner/gas/rules/tls_config.go
generated
vendored
Normal file
141
vendor/github.com/GoASTScanner/gas/rules/tls_config.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// NewModernTLSCheck creates a check for Modern TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewModernTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0303,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
||||
|
||||
// NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewIntermediateTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0301,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
||||
|
||||
// NewOldTLSCheck creates a check for Old TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewOldTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0300,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_SEED_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_SEED_CBC_SHA",
|
||||
"TLS_RSA_WITH_SEED_CBC_SHA",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
53
vendor/github.com/GoASTScanner/gas/rules/unsafe.go
generated
vendored
Normal file
53
vendor/github.com/GoASTScanner/gas/rules/unsafe.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type usingUnsafe struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *usingUnsafe) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsingUnsafe rule detects the use of the unsafe package. This is only
|
||||
// really useful for auditing purposes.
|
||||
func NewUsingUnsafe(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &usingUnsafe{
|
||||
pkg: "unsafe",
|
||||
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
What: "Use of unsafe calls should be audited",
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
57
vendor/github.com/GoASTScanner/gas/rules/weakcrypto.go
generated
vendored
Normal file
57
vendor/github.com/GoASTScanner/gas/rules/weakcrypto.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// (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.
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type usesWeakCryptography struct {
|
||||
gas.MetaData
|
||||
blacklist map[string][]string
|
||||
}
|
||||
|
||||
func (r *usesWeakCryptography) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
for pkg, funcs := range r.blacklist {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.*
|
||||
func NewUsesWeakCryptography(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := make(map[string][]string)
|
||||
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
||||
calls["crypto/md5"] = []string{"New", "Sum"}
|
||||
calls["crypto/rc4"] = []string{"NewCipher"}
|
||||
rule := &usesWeakCryptography{
|
||||
blacklist: calls,
|
||||
MetaData: gas.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "Use of weak cryptographic primitive",
|
||||
},
|
||||
}
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
2
vendor/github.com/nbutton23/zxcvbn-go/.gitignore
generated
vendored
Normal file
2
vendor/github.com/nbutton23/zxcvbn-go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
zxcvbn
|
||||
debug.test
|
27
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.lock
generated
vendored
Normal file
27
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b750880cdc8ce044e6f9bf3b331d8a392471c328107b8c3d42e3e11022d76858"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
26
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.toml
generated
vendored
Normal file
26
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.1.4"
|
20
vendor/github.com/nbutton23/zxcvbn-go/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/nbutton23/zxcvbn-go/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) Nathan Button
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
78
vendor/github.com/nbutton23/zxcvbn-go/README.md
generated
vendored
Normal file
78
vendor/github.com/nbutton23/zxcvbn-go/README.md
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
This is a goLang port of python-zxcvbn and [zxcvbn](https://github.com/dropbox/zxcvbn), which are python and JavaScript password strength
|
||||
generators. zxcvbn attempts to give sound password advice through pattern
|
||||
matching and conservative entropy calculations. It finds 10k common passwords,
|
||||
common American names and surnames, common English words, and common patterns
|
||||
like dates, repeats (aaa), sequences (abcd), and QWERTY patterns.
|
||||
|
||||
Please refer to http://tech.dropbox.com/?p=165 for the full details and
|
||||
motivation behind zxcbvn. The source code for the original JavaScript (well,
|
||||
actually CoffeeScript) implementation can be found at:
|
||||
|
||||
https://github.com/lowe/zxcvbn
|
||||
|
||||
Python at:
|
||||
|
||||
https://github.com/dropbox/python-zxcvbn
|
||||
|
||||
For full motivation, see:
|
||||
|
||||
http://tech.dropbox.com/?p=165
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Use
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The zxcvbn module has the public method PasswordStrength() function. Import zxcvbn, and
|
||||
call PasswordStrength(password string, userInputs []string). The function will return a
|
||||
result dictionary with the following keys:
|
||||
|
||||
Entropy # bits
|
||||
|
||||
CrackTime # estimation of actual crack time, in seconds.
|
||||
|
||||
CrackTimeDisplay # same crack time, as a friendlier string:
|
||||
# "instant", "6 minutes", "centuries", etc.
|
||||
|
||||
Score # [0,1,2,3,4] if crack time is less than
|
||||
# [10^2, 10^4, 10^6, 10^8, Infinity].
|
||||
# (useful for implementing a strength bar.)
|
||||
|
||||
MatchSequence # the list of patterns that zxcvbn based the
|
||||
# entropy calculation on.
|
||||
|
||||
CalcTime # how long it took to calculate an answer,
|
||||
# in milliseconds. usually only a few ms.
|
||||
|
||||
The userInputs argument is an splice of strings that zxcvbn
|
||||
will add to its internal dictionary. This can be whatever list of
|
||||
strings you like, but is meant for user inputs from other fields of the
|
||||
form, like name and email. That way a password that includes the user's
|
||||
personal info can be heavily penalized. This list is also good for
|
||||
site-specific vocabulary.
|
||||
|
||||
Bug reports and pull requests welcome!
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Project Status
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Use zxcvbn_test.go to check how close to feature parity the project is.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Acknowledgment
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Thanks to Dan Wheeler (https://github.com/lowe) for the CoffeeScript implementation
|
||||
(see above.) To repeat his outside acknowledgements (which remain useful, as always):
|
||||
|
||||
Many thanks to Mark Burnett for releasing his 10k top passwords list:
|
||||
http://xato.net/passwords/more-top-worst-passwords
|
||||
and for his 2006 book,
|
||||
"Perfect Passwords: Selection, Protection, Authentication"
|
||||
|
||||
Huge thanks to Wiktionary contributors for building a frequency list
|
||||
of English as used in television and movies:
|
||||
http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists
|
||||
|
||||
Last but not least, big thanks to xkcd :)
|
||||
https://xkcd.com/936/
|
96
vendor/github.com/nbutton23/zxcvbn-go/adjacency/adjcmartix.go
generated
vendored
Normal file
96
vendor/github.com/nbutton23/zxcvbn-go/adjacency/adjcmartix.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
package adjacency
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
// "fmt"
|
||||
"github.com/nbutton23/zxcvbn-go/data"
|
||||
)
|
||||
|
||||
type AdjacencyGraph struct {
|
||||
Graph map[string][]string
|
||||
averageDegree float64
|
||||
Name string
|
||||
}
|
||||
|
||||
var AdjacencyGph = make(map[string]AdjacencyGraph)
|
||||
|
||||
func init() {
|
||||
AdjacencyGph["qwerty"] = BuildQwerty()
|
||||
AdjacencyGph["dvorak"] = BuildDvorak()
|
||||
AdjacencyGph["keypad"] = BuildKeypad()
|
||||
AdjacencyGph["macKeypad"] = BuildMacKeypad()
|
||||
AdjacencyGph["l33t"] = BuildLeet()
|
||||
}
|
||||
|
||||
func BuildQwerty() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Qwerty.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "qwerty")
|
||||
}
|
||||
func BuildDvorak() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Dvorak.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "dvorak")
|
||||
}
|
||||
func BuildKeypad() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Keypad.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "keypad")
|
||||
}
|
||||
func BuildMacKeypad() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/MacKeypad.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "mac_keypad")
|
||||
}
|
||||
func BuildLeet() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/L33t.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "keypad")
|
||||
}
|
||||
|
||||
func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {
|
||||
|
||||
var graph AdjacencyGraph
|
||||
err := json.Unmarshal(data, &graph)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
graph.Name = name
|
||||
return graph
|
||||
}
|
||||
|
||||
//on qwerty, 'g' has degree 6, being adjacent to 'ftyhbv'. '\' has degree 1.
|
||||
//this calculates the average over all keys.
|
||||
//TODO double check that i ported this correctly scoring.coffee ln 5
|
||||
func (adjGrp AdjacencyGraph) CalculateAvgDegree() float64 {
|
||||
if adjGrp.averageDegree != float64(0) {
|
||||
return adjGrp.averageDegree
|
||||
}
|
||||
var avg float64
|
||||
var count float64
|
||||
for _, value := range adjGrp.Graph {
|
||||
|
||||
for _, char := range value {
|
||||
if char != "" || char != " " {
|
||||
avg += float64(len(char))
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
adjGrp.averageDegree = avg / count
|
||||
|
||||
return adjGrp.averageDegree
|
||||
}
|
444
vendor/github.com/nbutton23/zxcvbn-go/data/bindata.go
generated
vendored
Normal file
444
vendor/github.com/nbutton23/zxcvbn-go/data/bindata.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
215
vendor/github.com/nbutton23/zxcvbn-go/entropy/entropyCalculator.go
generated
vendored
Normal file
215
vendor/github.com/nbutton23/zxcvbn-go/entropy/entropyCalculator.go
generated
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"github.com/nbutton23/zxcvbn-go/adjacency"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
"github.com/nbutton23/zxcvbn-go/utils/math"
|
||||
"math"
|
||||
"regexp"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
START_UPPER string = `^[A-Z][^A-Z]+$`
|
||||
END_UPPER string = `^[^A-Z]+[A-Z]$'`
|
||||
ALL_UPPER string = `^[A-Z]+$`
|
||||
NUM_YEARS = float64(119) // years match against 1900 - 2019
|
||||
NUM_MONTHS = float64(12)
|
||||
NUM_DAYS = float64(31)
|
||||
)
|
||||
|
||||
var (
|
||||
KEYPAD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["keypad"].Graph)
|
||||
KEYPAD_AVG_DEGREE = adjacency.AdjacencyGph["keypad"].CalculateAvgDegree()
|
||||
)
|
||||
|
||||
func DictionaryEntropy(match match.Match, rank float64) float64 {
|
||||
baseEntropy := math.Log2(rank)
|
||||
upperCaseEntropy := extraUpperCaseEntropy(match)
|
||||
//TODO: L33t
|
||||
return baseEntropy + upperCaseEntropy
|
||||
}
|
||||
|
||||
func extraUpperCaseEntropy(match match.Match) float64 {
|
||||
word := match.Token
|
||||
|
||||
allLower := true
|
||||
|
||||
for _, char := range word {
|
||||
if unicode.IsUpper(char) {
|
||||
allLower = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allLower {
|
||||
return float64(0)
|
||||
}
|
||||
|
||||
//a capitalized word is the most common capitalization scheme,
|
||||
//so it only doubles the search space (uncapitalized + capitalized): 1 extra bit of entropy.
|
||||
//allcaps and end-capitalized are common enough too, underestimate as 1 extra bit to be safe.
|
||||
|
||||
for _, regex := range []string{START_UPPER, END_UPPER, ALL_UPPER} {
|
||||
matcher := regexp.MustCompile(regex)
|
||||
|
||||
if matcher.MatchString(word) {
|
||||
return float64(1)
|
||||
}
|
||||
}
|
||||
//Otherwise calculate the number of ways to capitalize U+L uppercase+lowercase letters with U uppercase letters or
|
||||
//less. Or, if there's more uppercase than lower (for e.g. PASSwORD), the number of ways to lowercase U+L letters
|
||||
//with L lowercase letters or less.
|
||||
|
||||
countUpper, countLower := float64(0), float64(0)
|
||||
for _, char := range word {
|
||||
if unicode.IsUpper(char) {
|
||||
countUpper++
|
||||
} else if unicode.IsLower(char) {
|
||||
countLower++
|
||||
}
|
||||
}
|
||||
totalLenght := countLower + countUpper
|
||||
var possibililities float64
|
||||
|
||||
for i := float64(0); i <= math.Min(countUpper, countLower); i++ {
|
||||
possibililities += float64(zxcvbn_math.NChoseK(totalLenght, i))
|
||||
}
|
||||
|
||||
if possibililities < 1 {
|
||||
return float64(1)
|
||||
}
|
||||
|
||||
return float64(math.Log2(possibililities))
|
||||
}
|
||||
|
||||
func SpatialEntropy(match match.Match, turns int, shiftCount int) float64 {
|
||||
var s, d float64
|
||||
if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
|
||||
//todo: verify qwerty and dvorak have the same length and degree
|
||||
s = float64(len(adjacency.BuildQwerty().Graph))
|
||||
d = adjacency.BuildQwerty().CalculateAvgDegree()
|
||||
} else {
|
||||
s = float64(KEYPAD_STARTING_POSITIONS)
|
||||
d = KEYPAD_AVG_DEGREE
|
||||
}
|
||||
|
||||
possibilities := float64(0)
|
||||
|
||||
length := float64(len(match.Token))
|
||||
|
||||
//TODO: Should this be <= or just < ?
|
||||
//Estimate the number of possible patterns w/ length L or less with t turns or less
|
||||
for i := float64(2); i <= length+1; i++ {
|
||||
possibleTurns := math.Min(float64(turns), i-1)
|
||||
for j := float64(1); j <= possibleTurns+1; j++ {
|
||||
x := zxcvbn_math.NChoseK(i-1, j-1) * s * math.Pow(d, j)
|
||||
possibilities += x
|
||||
}
|
||||
}
|
||||
|
||||
entropy := math.Log2(possibilities)
|
||||
//add extra entropu for shifted keys. ( % instead of 5 A instead of a)
|
||||
//Math is similar to extra entropy for uppercase letters in dictionary matches.
|
||||
|
||||
if S := float64(shiftCount); S > float64(0) {
|
||||
possibilities = float64(0)
|
||||
U := length - S
|
||||
|
||||
for i := float64(0); i < math.Min(S, U)+1; i++ {
|
||||
possibilities += zxcvbn_math.NChoseK(S+U, i)
|
||||
}
|
||||
|
||||
entropy += math.Log2(possibilities)
|
||||
}
|
||||
|
||||
return entropy
|
||||
}
|
||||
|
||||
func RepeatEntropy(match match.Match) float64 {
|
||||
cardinality := CalcBruteForceCardinality(match.Token)
|
||||
entropy := math.Log2(cardinality * float64(len(match.Token)))
|
||||
|
||||
return entropy
|
||||
}
|
||||
|
||||
//TODO: Validate against python
|
||||
func CalcBruteForceCardinality(password string) float64 {
|
||||
lower, upper, digits, symbols := float64(0), float64(0), float64(0), float64(0)
|
||||
|
||||
for _, char := range password {
|
||||
if unicode.IsLower(char) {
|
||||
lower = float64(26)
|
||||
} else if unicode.IsDigit(char) {
|
||||
digits = float64(10)
|
||||
} else if unicode.IsUpper(char) {
|
||||
upper = float64(26)
|
||||
} else {
|
||||
symbols = float64(33)
|
||||
}
|
||||
}
|
||||
|
||||
cardinality := lower + upper + digits + symbols
|
||||
return cardinality
|
||||
}
|
||||
|
||||
func SequenceEntropy(match match.Match, dictionaryLength int, ascending bool) float64 {
|
||||
firstChar := match.Token[0]
|
||||
baseEntropy := float64(0)
|
||||
if string(firstChar) == "a" || string(firstChar) == "1" {
|
||||
baseEntropy = float64(0)
|
||||
} else {
|
||||
baseEntropy = math.Log2(float64(dictionaryLength))
|
||||
//TODO: should this be just the first or any char?
|
||||
if unicode.IsUpper(rune(firstChar)) {
|
||||
baseEntropy++
|
||||
}
|
||||
}
|
||||
|
||||
if !ascending {
|
||||
baseEntropy++
|
||||
}
|
||||
return baseEntropy + math.Log2(float64(len(match.Token)))
|
||||
}
|
||||
|
||||
func ExtraLeetEntropy(match match.Match, password string) float64 {
|
||||
var subsitutions float64
|
||||
var unsub float64
|
||||
subPassword := password[match.I:match.J]
|
||||
for index, char := range subPassword {
|
||||
if string(char) != string(match.Token[index]) {
|
||||
subsitutions++
|
||||
} else {
|
||||
//TODO: Make this only true for 1337 chars that are not subs?
|
||||
unsub++
|
||||
}
|
||||
}
|
||||
|
||||
var possibilities float64
|
||||
|
||||
for i := float64(0); i <= math.Min(subsitutions, unsub)+1; i++ {
|
||||
possibilities += zxcvbn_math.NChoseK(subsitutions+unsub, i)
|
||||
}
|
||||
|
||||
if possibilities <= 1 {
|
||||
return float64(1)
|
||||
}
|
||||
return math.Log2(possibilities)
|
||||
}
|
||||
|
||||
func YearEntropy(dateMatch match.DateMatch) float64 {
|
||||
return math.Log2(NUM_YEARS)
|
||||
}
|
||||
|
||||
func DateEntropy(dateMatch match.DateMatch) float64 {
|
||||
var entropy float64
|
||||
if dateMatch.Year < 100 {
|
||||
entropy = math.Log2(NUM_DAYS * NUM_MONTHS * 100)
|
||||
} else {
|
||||
entropy = math.Log2(NUM_DAYS * NUM_MONTHS * NUM_YEARS)
|
||||
}
|
||||
|
||||
if dateMatch.Separator != "" {
|
||||
entropy += 2 //add two bits for separator selection [/,-,.,etc]
|
||||
}
|
||||
return entropy
|
||||
}
|
47
vendor/github.com/nbutton23/zxcvbn-go/frequency/frequency.go
generated
vendored
Normal file
47
vendor/github.com/nbutton23/zxcvbn-go/frequency/frequency.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package frequency
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/nbutton23/zxcvbn-go/data"
|
||||
"log"
|
||||
)
|
||||
|
||||
type FrequencyList struct {
|
||||
Name string
|
||||
List []string
|
||||
}
|
||||
|
||||
var FrequencyLists = make(map[string]FrequencyList)
|
||||
|
||||
func init() {
|
||||
maleFilePath := getAsset("data/MaleNames.json")
|
||||
femaleFilePath := getAsset("data/FemaleNames.json")
|
||||
surnameFilePath := getAsset("data/Surnames.json")
|
||||
englishFilePath := getAsset("data/English.json")
|
||||
passwordsFilePath := getAsset("data/Passwords.json")
|
||||
|
||||
FrequencyLists["MaleNames"] = GetStringListFromAsset(maleFilePath, "MaleNames")
|
||||
FrequencyLists["FemaleNames"] = GetStringListFromAsset(femaleFilePath, "FemaleNames")
|
||||
FrequencyLists["Surname"] = GetStringListFromAsset(surnameFilePath, "Surname")
|
||||
FrequencyLists["English"] = GetStringListFromAsset(englishFilePath, "English")
|
||||
FrequencyLists["Passwords"] = GetStringListFromAsset(passwordsFilePath, "Passwords")
|
||||
|
||||
}
|
||||
func getAsset(name string) []byte {
|
||||
data, err := zxcvbn_data.Asset(name)
|
||||
if err != nil {
|
||||
panic("Error getting asset " + name)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
func GetStringListFromAsset(data []byte, name string) FrequencyList {
|
||||
|
||||
var tempList FrequencyList
|
||||
err := json.Unmarshal(data, &tempList)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tempList.Name = name
|
||||
return tempList
|
||||
}
|
40
vendor/github.com/nbutton23/zxcvbn-go/match/match.go
generated
vendored
Normal file
40
vendor/github.com/nbutton23/zxcvbn-go/match/match.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package match
|
||||
|
||||
type Matches []Match
|
||||
|
||||
func (s Matches) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s Matches) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s Matches) Less(i, j int) bool {
|
||||
if s[i].I < s[j].I {
|
||||
return true
|
||||
} else if s[i].I == s[j].I {
|
||||
return s[i].J < s[j].J
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type Match struct {
|
||||
Pattern string
|
||||
I, J int
|
||||
Token string
|
||||
DictionaryName string
|
||||
Entropy float64
|
||||
}
|
||||
|
||||
type DateMatch struct {
|
||||
Pattern string
|
||||
I, J int
|
||||
Token string
|
||||
Separator string
|
||||
Day, Month, Year int64
|
||||
}
|
||||
|
||||
type Matcher struct {
|
||||
MatchingFunc func(password string) []Match
|
||||
ID string
|
||||
}
|
204
vendor/github.com/nbutton23/zxcvbn-go/matching/dateMatchers.go
generated
vendored
Normal file
204
vendor/github.com/nbutton23/zxcvbn-go/matching/dateMatchers.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
const (
|
||||
DATESEP_MATCHER_NAME = "DATESEP"
|
||||
DATEWITHOUTSEP_MATCHER_NAME = "DATEWITHOUT"
|
||||
)
|
||||
|
||||
func FilterDateSepMatcher(m match.Matcher) bool {
|
||||
return m.ID == DATESEP_MATCHER_NAME
|
||||
}
|
||||
|
||||
func FilterDateWithoutSepMatcher(m match.Matcher) bool {
|
||||
return m.ID == DATEWITHOUTSEP_MATCHER_NAME
|
||||
}
|
||||
|
||||
func checkDate(day, month, year int64) (bool, int64, int64, int64) {
|
||||
if (12 <= month && month <= 31) && day <= 12 {
|
||||
day, month = month, day
|
||||
}
|
||||
|
||||
if day > 31 || month > 12 {
|
||||
return false, 0, 0, 0
|
||||
}
|
||||
|
||||
if !((1900 <= year && year <= 2019) || (0 <= year && year <= 99)) {
|
||||
return false, 0, 0, 0
|
||||
}
|
||||
|
||||
return true, day, month, year
|
||||
}
|
||||
|
||||
func dateSepMatcher(password string) []match.Match {
|
||||
dateMatches := dateSepMatchHelper(password)
|
||||
|
||||
var matches []match.Match
|
||||
for _, dateMatch := range dateMatches {
|
||||
match := match.Match{
|
||||
I: dateMatch.I,
|
||||
J: dateMatch.J,
|
||||
Entropy: entropy.DateEntropy(dateMatch),
|
||||
DictionaryName: "date_match",
|
||||
Token: dateMatch.Token,
|
||||
}
|
||||
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
func dateSepMatchHelper(password string) []match.DateMatch {
|
||||
|
||||
var matches []match.DateMatch
|
||||
|
||||
matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX)
|
||||
for _, v := range matcher.FindAllString(password, len(password)) {
|
||||
splitV := matcher.FindAllStringSubmatch(v, len(v))
|
||||
i := strings.Index(password, v)
|
||||
j := i + len(v)
|
||||
day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
|
||||
month, _ := strconv.ParseInt(splitV[0][2], 10, 16)
|
||||
year, _ := strconv.ParseInt(splitV[0][6], 10, 16)
|
||||
match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX)
|
||||
for _, v := range matcher.FindAllString(password, len(password)) {
|
||||
splitV := matcher.FindAllStringSubmatch(v, len(v))
|
||||
i := strings.Index(password, v)
|
||||
j := i + len(v)
|
||||
day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
|
||||
month, _ := strconv.ParseInt(splitV[0][6], 10, 16)
|
||||
year, _ := strconv.ParseInt(splitV[0][2], 10, 16)
|
||||
match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
var out []match.DateMatch
|
||||
for _, match := range matches {
|
||||
if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid {
|
||||
match.Pattern = "date"
|
||||
match.Day = day
|
||||
match.Month = month
|
||||
match.Year = year
|
||||
out = append(out, match)
|
||||
}
|
||||
}
|
||||
return out
|
||||
|
||||
}
|
||||
|
||||
type DateMatchCandidate struct {
|
||||
DayMonth string
|
||||
Year string
|
||||
I, J int
|
||||
}
|
||||
|
||||
type DateMatchCandidateTwo struct {
|
||||
Day string
|
||||
Month string
|
||||
Year string
|
||||
I, J int
|
||||
}
|
||||
|
||||
func dateWithoutSepMatch(password string) []match.Match {
|
||||
dateMatches := dateWithoutSepMatchHelper(password)
|
||||
|
||||
var matches []match.Match
|
||||
for _, dateMatch := range dateMatches {
|
||||
match := match.Match{
|
||||
I: dateMatch.I,
|
||||
J: dateMatch.J,
|
||||
Entropy: entropy.DateEntropy(dateMatch),
|
||||
DictionaryName: "date_match",
|
||||
Token: dateMatch.Token,
|
||||
}
|
||||
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
//TODO Has issues with 6 digit dates
|
||||
func dateWithoutSepMatchHelper(password string) (matches []match.DateMatch) {
|
||||
matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
|
||||
for _, v := range matcher.FindAllString(password, len(password)) {
|
||||
i := strings.Index(password, v)
|
||||
j := i + len(v)
|
||||
length := len(v)
|
||||
lastIndex := length - 1
|
||||
var candidatesRoundOne []DateMatchCandidate
|
||||
|
||||
if length <= 6 {
|
||||
//2-digit year prefix
|
||||
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[2:], v[0:2], i, j))
|
||||
|
||||
//2-digityear suffix
|
||||
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-2], v[lastIndex-2:], i, j))
|
||||
}
|
||||
if length >= 6 {
|
||||
//4-digit year prefix
|
||||
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[4:], v[0:4], i, j))
|
||||
|
||||
//4-digit year sufix
|
||||
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-3], v[lastIndex-3:], i, j))
|
||||
}
|
||||
|
||||
var candidatesRoundTwo []DateMatchCandidateTwo
|
||||
for _, c := range candidatesRoundOne {
|
||||
if len(c.DayMonth) == 2 {
|
||||
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:1], c.Year, c.I, c.J))
|
||||
} else if len(c.DayMonth) == 3 {
|
||||
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:2], c.Year, c.I, c.J))
|
||||
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:3], c.Year, c.I, c.J))
|
||||
} else if len(c.DayMonth) == 4 {
|
||||
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:4], c.Year, c.I, c.J))
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidatesRoundTwo {
|
||||
intDay, err := strconv.ParseInt(candidate.Day, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
intMonth, err := strconv.ParseInt(candidate.Month, 10, 16)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
intYear, err := strconv.ParseInt(candidate.Year, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ok, _, _, _ := checkDate(intDay, intMonth, intYear); ok {
|
||||
matches = append(matches, match.DateMatch{Token: password, Pattern: "date", Day: intDay, Month: intMonth, Year: intYear, I: i, J: j})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
func buildDateMatchCandidate(dayMonth, year string, i, j int) DateMatchCandidate {
|
||||
return DateMatchCandidate{DayMonth: dayMonth, Year: year, I: i, J: j}
|
||||
}
|
||||
|
||||
func buildDateMatchCandidateTwo(day, month string, year string, i, j int) DateMatchCandidateTwo {
|
||||
|
||||
return DateMatchCandidateTwo{Day: day, Month: month, Year: year, I: i, J: j}
|
||||
}
|
54
vendor/github.com/nbutton23/zxcvbn-go/matching/dictionaryMatch.go
generated
vendored
Normal file
54
vendor/github.com/nbutton23/zxcvbn-go/matching/dictionaryMatch.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []match.Match {
|
||||
return func(password string) []match.Match {
|
||||
matches := dictionaryMatch(password, dictName, rankedDict)
|
||||
for _, v := range matches {
|
||||
v.DictionaryName = dictName
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func dictionaryMatch(password string, dictionaryName string, rankedDict map[string]int) []match.Match {
|
||||
length := len(password)
|
||||
var results []match.Match
|
||||
pwLower := strings.ToLower(password)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
for j := i; j < length; j++ {
|
||||
word := pwLower[i : j+1]
|
||||
if val, ok := rankedDict[word]; ok {
|
||||
matchDic := match.Match{Pattern: "dictionary",
|
||||
DictionaryName: dictionaryName,
|
||||
I: i,
|
||||
J: j,
|
||||
Token: password[i : j+1],
|
||||
}
|
||||
matchDic.Entropy = entropy.DictionaryEntropy(matchDic, float64(val))
|
||||
|
||||
results = append(results, matchDic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func buildRankedDict(unrankedList []string) map[string]int {
|
||||
|
||||
result := make(map[string]int)
|
||||
|
||||
for i, v := range unrankedList {
|
||||
result[strings.ToLower(v)] = i + 1
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
75
vendor/github.com/nbutton23/zxcvbn-go/matching/leet.go
generated
vendored
Normal file
75
vendor/github.com/nbutton23/zxcvbn-go/matching/leet.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
const L33T_MATCHER_NAME = "l33t"
|
||||
|
||||
func FilterL33tMatcher(m match.Matcher) bool {
|
||||
return m.ID == L33T_MATCHER_NAME
|
||||
}
|
||||
|
||||
func l33tMatch(password string) []match.Match {
|
||||
|
||||
substitutions := relevantL33tSubtable(password)
|
||||
|
||||
permutations := getAllPermutationsOfLeetSubstitutions(password, substitutions)
|
||||
|
||||
var matches []match.Match
|
||||
|
||||
for _, permutation := range permutations {
|
||||
for _, mather := range DICTIONARY_MATCHERS {
|
||||
matches = append(matches, mather.MatchingFunc(permutation)...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
match.Entropy += entropy.ExtraLeetEntropy(match, password)
|
||||
match.DictionaryName = match.DictionaryName + "_3117"
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
func getAllPermutationsOfLeetSubstitutions(password string, substitutionsMap map[string][]string) []string {
|
||||
|
||||
var permutations []string
|
||||
|
||||
for index, char := range password {
|
||||
for value, splice := range substitutionsMap {
|
||||
for _, sub := range splice {
|
||||
if string(char) == sub {
|
||||
var permutation string
|
||||
permutation = password[:index] + value + password[index+1:]
|
||||
|
||||
permutations = append(permutations, permutation)
|
||||
if index < len(permutation) {
|
||||
tempPermutations := getAllPermutationsOfLeetSubstitutions(permutation[index+1:], substitutionsMap)
|
||||
for _, temp := range tempPermutations {
|
||||
permutations = append(permutations, permutation[:index+1]+temp)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permutations
|
||||
}
|
||||
|
||||
func relevantL33tSubtable(password string) map[string][]string {
|
||||
relevantSubs := make(map[string][]string)
|
||||
for key, values := range L33T_TABLE.Graph {
|
||||
for _, value := range values {
|
||||
if strings.Contains(password, value) {
|
||||
relevantSubs[key] = append(relevantSubs[key], value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return relevantSubs
|
||||
}
|
87
vendor/github.com/nbutton23/zxcvbn-go/matching/matching.go
generated
vendored
Normal file
87
vendor/github.com/nbutton23/zxcvbn-go/matching/matching.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/adjacency"
|
||||
"github.com/nbutton23/zxcvbn-go/frequency"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
var (
|
||||
DICTIONARY_MATCHERS []match.Matcher
|
||||
MATCHERS []match.Matcher
|
||||
ADJACENCY_GRAPHS []adjacency.AdjacencyGraph
|
||||
L33T_TABLE adjacency.AdjacencyGraph
|
||||
|
||||
SEQUENCES map[string]string
|
||||
)
|
||||
|
||||
const (
|
||||
DATE_RX_YEAR_SUFFIX string = `((\d{1,2})(\s|-|\/|\\|_|\.)(\d{1,2})(\s|-|\/|\\|_|\.)(19\d{2}|200\d|201\d|\d{2}))`
|
||||
DATE_RX_YEAR_PREFIX string = `((19\d{2}|200\d|201\d|\d{2})(\s|-|/|\\|_|\.)(\d{1,2})(\s|-|/|\\|_|\.)(\d{1,2}))`
|
||||
DATE_WITHOUT_SEP_MATCH string = `\d{4,8}`
|
||||
)
|
||||
|
||||
func init() {
|
||||
loadFrequencyList()
|
||||
}
|
||||
|
||||
func Omnimatch(password string, userInputs []string, filters ...func(match.Matcher) bool) (matches []match.Match) {
|
||||
|
||||
//Can I run into the issue where nil is not equal to nil?
|
||||
if DICTIONARY_MATCHERS == nil || ADJACENCY_GRAPHS == nil {
|
||||
loadFrequencyList()
|
||||
}
|
||||
|
||||
if userInputs != nil {
|
||||
userInputMatcher := buildDictMatcher("user_inputs", buildRankedDict(userInputs))
|
||||
matches = userInputMatcher(password)
|
||||
}
|
||||
|
||||
for _, matcher := range MATCHERS {
|
||||
shouldBeFiltered := false
|
||||
for i := range filters {
|
||||
if filters[i](matcher) {
|
||||
shouldBeFiltered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !shouldBeFiltered {
|
||||
matches = append(matches, matcher.MatchingFunc(password)...)
|
||||
}
|
||||
}
|
||||
sort.Sort(match.Matches(matches))
|
||||
return matches
|
||||
}
|
||||
|
||||
func loadFrequencyList() {
|
||||
|
||||
for n, list := range frequency.FrequencyLists {
|
||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, match.Matcher{MatchingFunc: buildDictMatcher(n, buildRankedDict(list.List)), ID: n})
|
||||
}
|
||||
|
||||
L33T_TABLE = adjacency.AdjacencyGph["l33t"]
|
||||
|
||||
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["qwerty"])
|
||||
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["dvorak"])
|
||||
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["keypad"])
|
||||
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["macKeypad"])
|
||||
|
||||
//l33tFilePath, _ := filepath.Abs("adjacency/L33t.json")
|
||||
//L33T_TABLE = adjacency.GetAdjancencyGraphFromFile(l33tFilePath, "l33t")
|
||||
|
||||
SEQUENCES = make(map[string]string)
|
||||
SEQUENCES["lower"] = "abcdefghijklmnopqrstuvwxyz"
|
||||
SEQUENCES["upper"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SEQUENCES["digits"] = "0123456789"
|
||||
|
||||
MATCHERS = append(MATCHERS, DICTIONARY_MATCHERS...)
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: spatialMatch, ID: SPATIAL_MATCHER_NAME})
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: repeatMatch, ID: REPEAT_MATCHER_NAME})
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: sequenceMatch, ID: SEQUENCE_MATCHER_NAME})
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: l33tMatch, ID: L33T_MATCHER_NAME})
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: dateSepMatcher, ID: DATESEP_MATCHER_NAME})
|
||||
MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: dateWithoutSepMatch, ID: DATEWITHOUTSEP_MATCHER_NAME})
|
||||
|
||||
}
|
66
vendor/github.com/nbutton23/zxcvbn-go/matching/repeatMatch.go
generated
vendored
Normal file
66
vendor/github.com/nbutton23/zxcvbn-go/matching/repeatMatch.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
const REPEAT_MATCHER_NAME = "REPEAT"
|
||||
|
||||
func FilterRepeatMatcher(m match.Matcher) bool {
|
||||
return m.ID == REPEAT_MATCHER_NAME
|
||||
}
|
||||
|
||||
func repeatMatch(password string) []match.Match {
|
||||
var matches []match.Match
|
||||
|
||||
//Loop through password. if current == prev currentStreak++ else if currentStreak > 2 {buildMatch; currentStreak = 1} prev = current
|
||||
var current, prev string
|
||||
currentStreak := 1
|
||||
var i int
|
||||
var char rune
|
||||
for i, char = range password {
|
||||
current = string(char)
|
||||
if i == 0 {
|
||||
prev = current
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.ToLower(current) == strings.ToLower(prev) {
|
||||
currentStreak++
|
||||
|
||||
} else if currentStreak > 2 {
|
||||
iPos := i - currentStreak
|
||||
jPos := i - 1
|
||||
matchRepeat := match.Match{
|
||||
Pattern: "repeat",
|
||||
I: iPos,
|
||||
J: jPos,
|
||||
Token: password[iPos : jPos+1],
|
||||
DictionaryName: prev}
|
||||
matchRepeat.Entropy = entropy.RepeatEntropy(matchRepeat)
|
||||
matches = append(matches, matchRepeat)
|
||||
currentStreak = 1
|
||||
} else {
|
||||
currentStreak = 1
|
||||
}
|
||||
|
||||
prev = current
|
||||
}
|
||||
|
||||
if currentStreak > 2 {
|
||||
iPos := i - currentStreak + 1
|
||||
jPos := i
|
||||
matchRepeat := match.Match{
|
||||
Pattern: "repeat",
|
||||
I: iPos,
|
||||
J: jPos,
|
||||
Token: password[iPos : jPos+1],
|
||||
DictionaryName: prev}
|
||||
matchRepeat.Entropy = entropy.RepeatEntropy(matchRepeat)
|
||||
matches = append(matches, matchRepeat)
|
||||
}
|
||||
return matches
|
||||
}
|
75
vendor/github.com/nbutton23/zxcvbn-go/matching/sequenceMatch.go
generated
vendored
Normal file
75
vendor/github.com/nbutton23/zxcvbn-go/matching/sequenceMatch.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
const SEQUENCE_MATCHER_NAME = "SEQ"
|
||||
|
||||
func FilterSequenceMatcher(m match.Matcher) bool {
|
||||
return m.ID == SEQUENCE_MATCHER_NAME
|
||||
}
|
||||
|
||||
func sequenceMatch(password string) []match.Match {
|
||||
var matches []match.Match
|
||||
for i := 0; i < len(password); {
|
||||
j := i + 1
|
||||
var seq string
|
||||
var seqName string
|
||||
seqDirection := 0
|
||||
for seqCandidateName, seqCandidate := range SEQUENCES {
|
||||
iN := strings.Index(seqCandidate, string(password[i]))
|
||||
var jN int
|
||||
if j < len(password) {
|
||||
jN = strings.Index(seqCandidate, string(password[j]))
|
||||
} else {
|
||||
jN = -1
|
||||
}
|
||||
|
||||
if iN > -1 && jN > -1 {
|
||||
direction := jN - iN
|
||||
if direction == 1 || direction == -1 {
|
||||
seq = seqCandidate
|
||||
seqName = seqCandidateName
|
||||
seqDirection = direction
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if seq != "" {
|
||||
for {
|
||||
var prevN, curN int
|
||||
if j < len(password) {
|
||||
prevChar, curChar := password[j-1], password[j]
|
||||
prevN, curN = strings.Index(seq, string(prevChar)), strings.Index(seq, string(curChar))
|
||||
}
|
||||
|
||||
if j == len(password) || curN-prevN != seqDirection {
|
||||
if j-i > 2 {
|
||||
matchSequence := match.Match{
|
||||
Pattern: "sequence",
|
||||
I: i,
|
||||
J: j - 1,
|
||||
Token: password[i:j],
|
||||
DictionaryName: seqName,
|
||||
}
|
||||
|
||||
matchSequence.Entropy = entropy.SequenceEntropy(matchSequence, len(seq), (seqDirection == 1))
|
||||
matches = append(matches, matchSequence)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
j += 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
i = j
|
||||
}
|
||||
return matches
|
||||
}
|
87
vendor/github.com/nbutton23/zxcvbn-go/matching/spatialMatch.go
generated
vendored
Normal file
87
vendor/github.com/nbutton23/zxcvbn-go/matching/spatialMatch.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/adjacency"
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
)
|
||||
|
||||
const SPATIAL_MATCHER_NAME = "SPATIAL"
|
||||
|
||||
func FilterSpatialMatcher(m match.Matcher) bool {
|
||||
return m.ID == SPATIAL_MATCHER_NAME
|
||||
}
|
||||
|
||||
func spatialMatch(password string) (matches []match.Match) {
|
||||
for _, graph := range ADJACENCY_GRAPHS {
|
||||
if graph.Graph != nil {
|
||||
matches = append(matches, spatialMatchHelper(password, graph)...)
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matches []match.Match) {
|
||||
|
||||
for i := 0; i < len(password)-1; {
|
||||
j := i + 1
|
||||
lastDirection := -99 //an int that it should never be!
|
||||
turns := 0
|
||||
shiftedCount := 0
|
||||
|
||||
for {
|
||||
prevChar := password[j-1]
|
||||
found := false
|
||||
foundDirection := -1
|
||||
curDirection := -1
|
||||
//My graphs seem to be wrong. . . and where the hell is qwerty
|
||||
adjacents := graph.Graph[string(prevChar)]
|
||||
//Consider growing pattern by one character if j hasn't gone over the edge
|
||||
if j < len(password) {
|
||||
curChar := password[j]
|
||||
for _, adj := range adjacents {
|
||||
curDirection += 1
|
||||
|
||||
if strings.Index(adj, string(curChar)) != -1 {
|
||||
found = true
|
||||
foundDirection = curDirection
|
||||
|
||||
if strings.Index(adj, string(curChar)) == 1 {
|
||||
//index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
|
||||
//for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
|
||||
shiftedCount += 1
|
||||
}
|
||||
|
||||
if lastDirection != foundDirection {
|
||||
//adding a turn is correct even in the initial case when last_direction is null:
|
||||
//every spatial pattern starts with a turn.
|
||||
turns += 1
|
||||
lastDirection = foundDirection
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if the current pattern continued, extend j and try to grow again
|
||||
if found {
|
||||
j += 1
|
||||
} else {
|
||||
//otherwise push the pattern discovered so far, if any...
|
||||
//don't consider length 1 or 2 chains.
|
||||
if j-i > 2 {
|
||||
matchSpc := match.Match{Pattern: "spatial", I: i, J: j - 1, Token: password[i:j], DictionaryName: graph.Name}
|
||||
matchSpc.Entropy = entropy.SpatialEntropy(matchSpc, turns, shiftedCount)
|
||||
matches = append(matches, matchSpc)
|
||||
}
|
||||
//. . . and then start a new search from the rest of the password
|
||||
i = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return matches
|
||||
}
|
180
vendor/github.com/nbutton23/zxcvbn-go/scoring/scoring.go
generated
vendored
Normal file
180
vendor/github.com/nbutton23/zxcvbn-go/scoring/scoring.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
package scoring
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nbutton23/zxcvbn-go/entropy"
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
"github.com/nbutton23/zxcvbn-go/utils/math"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
START_UPPER string = `^[A-Z][^A-Z]+$`
|
||||
END_UPPER string = `^[^A-Z]+[A-Z]$'`
|
||||
ALL_UPPER string = `^[A-Z]+$`
|
||||
|
||||
//for a hash function like bcrypt/scrypt/PBKDF2, 10ms per guess is a safe lower bound.
|
||||
//(usually a guess would take longer -- this assumes fast hardware and a small work factor.)
|
||||
//adjust for your site accordingly if you use another hash function, possibly by
|
||||
//several orders of magnitude!
|
||||
SINGLE_GUESS float64 = 0.010
|
||||
NUM_ATTACKERS float64 = 100 //Cores used to make guesses
|
||||
SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS
|
||||
)
|
||||
|
||||
type MinEntropyMatch struct {
|
||||
Password string
|
||||
Entropy float64
|
||||
MatchSequence []match.Match
|
||||
CrackTime float64
|
||||
CrackTimeDisplay string
|
||||
Score int
|
||||
CalcTime float64
|
||||
}
|
||||
|
||||
/*
|
||||
Returns minimum entropy
|
||||
|
||||
Takes a list of overlapping matches, returns the non-overlapping sublist with
|
||||
minimum entropy. O(nm) dp alg for length-n password with m candidate matches.
|
||||
*/
|
||||
func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntropyMatch {
|
||||
bruteforceCardinality := float64(entropy.CalcBruteForceCardinality(password))
|
||||
upToK := make([]float64, len(password))
|
||||
backPointers := make([]match.Match, len(password))
|
||||
|
||||
for k := 0; k < len(password); k++ {
|
||||
upToK[k] = get(upToK, k-1) + math.Log2(bruteforceCardinality)
|
||||
|
||||
for _, match := range matches {
|
||||
if match.J != k {
|
||||
continue
|
||||
}
|
||||
|
||||
i, j := match.I, match.J
|
||||
//see if best entropy up to i-1 + entropy of match is less that current min at j
|
||||
upTo := get(upToK, i-1)
|
||||
candidateEntropy := upTo + match.Entropy
|
||||
|
||||
if candidateEntropy < upToK[j] {
|
||||
upToK[j] = candidateEntropy
|
||||
match.Entropy = candidateEntropy
|
||||
backPointers[j] = match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//walk backwards and decode the best sequence
|
||||
var matchSequence []match.Match
|
||||
passwordLen := len(password)
|
||||
passwordLen--
|
||||
for k := passwordLen; k >= 0; {
|
||||
match := backPointers[k]
|
||||
if match.Pattern != "" {
|
||||
matchSequence = append(matchSequence, match)
|
||||
k = match.I - 1
|
||||
|
||||
} else {
|
||||
k--
|
||||
}
|
||||
|
||||
}
|
||||
sort.Sort(match.Matches(matchSequence))
|
||||
|
||||
makeBruteForceMatch := func(i, j int) match.Match {
|
||||
return match.Match{Pattern: "bruteforce",
|
||||
I: i,
|
||||
J: j,
|
||||
Token: password[i : j+1],
|
||||
Entropy: math.Log2(math.Pow(bruteforceCardinality, float64(j-i)))}
|
||||
|
||||
}
|
||||
|
||||
k := 0
|
||||
var matchSequenceCopy []match.Match
|
||||
for _, match := range matchSequence {
|
||||
i, j := match.I, match.J
|
||||
if i-k > 0 {
|
||||
matchSequenceCopy = append(matchSequenceCopy, makeBruteForceMatch(k, i-1))
|
||||
}
|
||||
k = j + 1
|
||||
matchSequenceCopy = append(matchSequenceCopy, match)
|
||||
}
|
||||
|
||||
if k < len(password) {
|
||||
matchSequenceCopy = append(matchSequenceCopy, makeBruteForceMatch(k, len(password)-1))
|
||||
}
|
||||
var minEntropy float64
|
||||
if len(password) == 0 {
|
||||
minEntropy = float64(0)
|
||||
} else {
|
||||
minEntropy = upToK[len(password)-1]
|
||||
}
|
||||
|
||||
crackTime := roundToXDigits(entropyToCrackTime(minEntropy), 3)
|
||||
return MinEntropyMatch{Password: password,
|
||||
Entropy: roundToXDigits(minEntropy, 3),
|
||||
MatchSequence: matchSequenceCopy,
|
||||
CrackTime: crackTime,
|
||||
CrackTimeDisplay: displayTime(crackTime),
|
||||
Score: crackTimeToScore(crackTime)}
|
||||
|
||||
}
|
||||
func get(a []float64, i int) float64 {
|
||||
if i < 0 || i >= len(a) {
|
||||
return float64(0)
|
||||
}
|
||||
|
||||
return a[i]
|
||||
}
|
||||
|
||||
func entropyToCrackTime(entropy float64) float64 {
|
||||
crackTime := (0.5 * math.Pow(float64(2), entropy)) * SECONDS_PER_GUESS
|
||||
|
||||
return crackTime
|
||||
}
|
||||
|
||||
func roundToXDigits(number float64, digits int) float64 {
|
||||
return zxcvbn_math.Round(number, .5, digits)
|
||||
}
|
||||
|
||||
func displayTime(seconds float64) string {
|
||||
formater := "%.1f %s"
|
||||
minute := float64(60)
|
||||
hour := minute * float64(60)
|
||||
day := hour * float64(24)
|
||||
month := day * float64(31)
|
||||
year := month * float64(12)
|
||||
century := year * float64(100)
|
||||
|
||||
if seconds < minute {
|
||||
return "instant"
|
||||
} else if seconds < hour {
|
||||
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/minute)), "minutes")
|
||||
} else if seconds < day {
|
||||
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/hour)), "hours")
|
||||
} else if seconds < month {
|
||||
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/day)), "days")
|
||||
} else if seconds < year {
|
||||
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/month)), "months")
|
||||
} else if seconds < century {
|
||||
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/century)), "years")
|
||||
} else {
|
||||
return "centuries"
|
||||
}
|
||||
}
|
||||
|
||||
func crackTimeToScore(seconds float64) int {
|
||||
if seconds < math.Pow(10, 2) {
|
||||
return 0
|
||||
} else if seconds < math.Pow(10, 4) {
|
||||
return 1
|
||||
} else if seconds < math.Pow(10, 6) {
|
||||
return 2
|
||||
} else if seconds < math.Pow(10, 8) {
|
||||
return 3
|
||||
}
|
||||
|
||||
return 4
|
||||
}
|
40
vendor/github.com/nbutton23/zxcvbn-go/utils/math/mathutils.go
generated
vendored
Normal file
40
vendor/github.com/nbutton23/zxcvbn-go/utils/math/mathutils.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package zxcvbn_math
|
||||
|
||||
import "math"
|
||||
|
||||
/**
|
||||
I am surprised that I have to define these. . . Maybe i just didn't look hard enough for a lib.
|
||||
*/
|
||||
|
||||
//http://blog.plover.com/math/choose.html
|
||||
func NChoseK(n, k float64) float64 {
|
||||
if k > n {
|
||||
return 0
|
||||
} else if k == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
var r float64 = 1
|
||||
|
||||
for d := float64(1); d <= k; d++ {
|
||||
r *= n
|
||||
r /= d
|
||||
n--
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func Round(val float64, roundOn float64, places int) (newVal float64) {
|
||||
var round float64
|
||||
pow := math.Pow(10, float64(places))
|
||||
digit := pow * val
|
||||
_, div := math.Modf(digit)
|
||||
if div >= roundOn {
|
||||
round = math.Ceil(digit)
|
||||
} else {
|
||||
round = math.Floor(digit)
|
||||
}
|
||||
newVal = round / pow
|
||||
return
|
||||
}
|
21
vendor/github.com/nbutton23/zxcvbn-go/zxcvbn.go
generated
vendored
Normal file
21
vendor/github.com/nbutton23/zxcvbn-go/zxcvbn.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package zxcvbn
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go/match"
|
||||
"github.com/nbutton23/zxcvbn-go/matching"
|
||||
"github.com/nbutton23/zxcvbn-go/scoring"
|
||||
"github.com/nbutton23/zxcvbn-go/utils/math"
|
||||
)
|
||||
|
||||
func PasswordStrength(password string, userInputs []string, filters ...func(match.Matcher) bool) scoring.MinEntropyMatch {
|
||||
start := time.Now()
|
||||
matches := matching.Omnimatch(password, userInputs, filters...)
|
||||
result := scoring.MinimumEntropyMatchSequence(password, matches)
|
||||
end := time.Now()
|
||||
|
||||
calcTime := end.Nanosecond() - start.Nanosecond()
|
||||
result.CalcTime = zxcvbn_math.Round(float64(calcTime)*time.Nanosecond.Seconds(), .5, 3)
|
||||
return result
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user