Skip to content

Commit 79408ba

Browse files
committed
Initial commit
0 parents  commit 79408ba

File tree

14 files changed

+288
-0
lines changed

14 files changed

+288
-0
lines changed

.github/workflows/go.yml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Go
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v2
15+
16+
- name: Set up Go
17+
uses: actions/setup-go@v2
18+
with:
19+
go-version: 1.17
20+
21+
- name: All
22+
run: make
23+
24+
- name: Test
25+
run: make test

.github/workflows/golangci-lint.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: golangci-lint
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
permissions:
8+
contents: read
9+
jobs:
10+
golangci:
11+
name: lint
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/setup-go@v2
15+
- uses: actions/checkout@v2
16+
- name: golangci-lint
17+
uses: golangci/golangci-lint-action@v2
18+
with:
19+
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
20+
version: latest

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
noemptyhttpproxy.so
2+
noemptyhttpproxy
3+

Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM docker.io/library/golang:1.18 as builder
2+
3+
COPY / /noemptyhttpproxy
4+
WORKDIR /noemptyhttpproxy
5+
RUN CGO_ENABLED=0 make
6+
7+
FROM docker.io/library/golang:1.18
8+
COPY --from=builder /noemptyhttpproxy/noemptyhttpproxy /usr/bin/
9+
CMD ["noemptyhttpproxy"]

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Stephen Benjamin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
all: noemptyhttpproxy.so noemptyhttpproxy
2+
.PHONY: lint test
3+
4+
clean:
5+
rm -f noemptyhttpproxy.so noemptyhttpproxy
6+
7+
test:
8+
go test ./...
9+
10+
lint:
11+
golangci-lint run ./...
12+
13+
noemptyhttpproxy:
14+
go build ./cmd/noemptyhttpproxy
15+
16+
noemptyhttpproxy.so:
17+
go build -buildmode=plugin ./plugin/noemptyhttpproxy.go

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# no-empty-http-proxy
2+
3+
When creating an HTTP Client with a custom transport, it's common to leave Proxy unspecified like this:
4+
5+
```go
6+
c := http.Client{
7+
Transport: &http.Transport{
8+
TLSClientConfig: tlsConfig,
9+
},
10+
}
11+
```
12+
13+
However, it's good practice to set Proxy to `http.ProxyFromEnvironment` like this:
14+
15+
```go
16+
c := http.Client{
17+
Transport: &http.Transport{
18+
TLSClientConfig: tlsConfig,
19+
Proxy: http.ProxyFromEnvironment,
20+
},
21+
}
22+
```
23+
24+
Leaving Proxy setting blank will cause problems later when someone inevitably tries to use your software with the
25+
standard proxy environment variables set, and finds out it doesn't work.
26+
27+
This linter forces a definition of the Proxy variable on an `http.Transport.` If you really absolutely do not want a
28+
proxy, explicitly set the field to `nil`.

cmd/noemptyhttpproxy/main.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
import (
4+
"golang.org/x/tools/go/analysis/singlechecker"
5+
6+
"github.com/stbenjam/no-empty-http-proxy/pkg/analyzer"
7+
)
8+
9+
func main() {
10+
singlechecker.Main(analyzer.Analyzer)
11+
}

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/stbenjam/no-empty-http-proxy
2+
3+
go 1.16
4+
5+
require golang.org/x/tools v0.1.10

go.sum

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
2+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
4+
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
5+
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
6+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
7+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
8+
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
9+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
10+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
11+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
12+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
13+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
14+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
15+
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
16+
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
18+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
19+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
20+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
21+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
22+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
23+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
24+
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
25+
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
26+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
27+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
28+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
29+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

pkg/analyzer/analyzer.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package analyzer
2+
3+
import (
4+
"go/ast"
5+
6+
"golang.org/x/tools/go/analysis/passes/inspect"
7+
"golang.org/x/tools/go/ast/inspector"
8+
9+
"golang.org/x/tools/go/analysis"
10+
)
11+
12+
var Analyzer = &analysis.Analyzer{
13+
Name: "noemptyhttpproxy",
14+
Doc: "Checks for lack of Proxy setting on HTTP Transport",
15+
Run: run,
16+
Requires: []*analysis.Analyzer{inspect.Analyzer},
17+
}
18+
19+
func run(pass *analysis.Pass) (interface{}, error) {
20+
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
21+
nodeFilter := []ast.Node{
22+
(*ast.CompositeLit)(nil),
23+
}
24+
25+
inspector.Preorder(nodeFilter, func(node ast.Node) {
26+
compLit := node.(*ast.CompositeLit)
27+
28+
if sel, ok := compLit.Type.(*ast.SelectorExpr); ok {
29+
gopkg, ok := sel.X.(*ast.Ident)
30+
if !ok {
31+
return
32+
}
33+
34+
if sel.Sel.Name == "Transport" && gopkg.Name == "http" {
35+
foundProxy := false
36+
for _, element := range compLit.Elts {
37+
if kvExpr, ok := element.(*ast.KeyValueExpr); ok {
38+
if ident, ok := kvExpr.Key.(*ast.Ident); ok && ident.Name == "Proxy" {
39+
foundProxy = true
40+
}
41+
}
42+
}
43+
44+
if !foundProxy {
45+
pass.Reportf(node.Pos(), "http.Transport should set Proxy; http.ProxyFromEnvironment is typical, but you may explicitly set to nil")
46+
}
47+
}
48+
}
49+
50+
})
51+
52+
return nil, nil
53+
}

pkg/analyzer/analyzer_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package analyzer
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"golang.org/x/tools/go/analysis/analysistest"
9+
)
10+
11+
func TestAll(t *testing.T) {
12+
wd, err := os.Getwd()
13+
if err != nil {
14+
t.Fatalf("Failed to get wd: %s", err)
15+
}
16+
17+
testdata := filepath.Join(filepath.Dir(filepath.Dir(wd)), "testdata")
18+
analysistest.Run(t, testdata, Analyzer, "p")
19+
}

plugin/noemptyhttpproxy.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"golang.org/x/tools/go/analysis"
5+
6+
"github.com/stbenjam/no-empty-http-proxy/pkg/analyzer"
7+
)
8+
9+
type analyzerPlugin struct{}
10+
11+
func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer {
12+
return []*analysis.Analyzer{
13+
analyzer.Analyzer,
14+
}
15+
}
16+
17+
// This must be defined and named 'AnalyzerPlugin'
18+
var AnalyzerPlugin analyzerPlugin //nolint

testdata/src/p/p.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package p
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
)
7+
8+
func _() {
9+
_ = http.Client{
10+
Transport: &http.Transport{}, // want "http.Transport should set Proxy"
11+
}
12+
13+
_ = http.Transport{} // want "http.Transport should set Proxy"
14+
15+
_ = http.Client{
16+
Transport: &http.Transport{
17+
Proxy: http.ProxyFromEnvironment,
18+
},
19+
}
20+
21+
_ = http.Transport{
22+
Proxy: func(*http.Request) (*url.URL, error) {
23+
return nil, nil
24+
},
25+
}
26+
27+
_ = http.Transport{
28+
Proxy: nil,
29+
}
30+
}

0 commit comments

Comments
 (0)