Skip to content

Commit

Permalink
go: weak scrypt cost
Browse files Browse the repository at this point in the history
  • Loading branch information
burntcarrot committed Feb 25, 2025
1 parent fcfe489 commit 626c1f5
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
2 changes: 2 additions & 0 deletions checkers/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"

goAnalysis "globstar.dev/analysis"
"globstar.dev/checkers/golang"
"globstar.dev/checkers/javascript"
"globstar.dev/pkg/analysis"
)
Expand Down Expand Up @@ -51,5 +52,6 @@ func LoadYamlRules() (map[analysis.Language][]analysis.YmlRule, error) {
func LoadGoRules() []*goAnalysis.Analyzer {
return []*goAnalysis.Analyzer{
&javascript.NoDoubleEq,
&golang.WeakBcryptCost,
}
}
71 changes: 71 additions & 0 deletions checkers/golang/weak_bcrypt_cost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package golang

import (
"strconv"

sitter "github.com/smacker/go-tree-sitter"
"globstar.dev/analysis"
)

var WeakBcryptCost analysis.Analyzer = analysis.Analyzer{
Name: "weak-bcrypt-cost",
Language: analysis.LangGo,
Description: "This rule checks for bcrypt usage with cost factor less than 10.",
Category: analysis.CategorySecurity,
Severity: analysis.SeverityWarning,
Run: weakBcryptCost,
}

func weakBcryptCost(pass *analysis.Pass) (interface{}, error) {
analysis.Preorder(pass, func(node *sitter.Node) {
if node.Type() != "call_expression" {
return
}

function := node.ChildByFieldName("function")
if function == nil || function.Type() != "selector_expression" {
return
}

operand := function.ChildByFieldName("operand")
field := function.ChildByFieldName("field")
if operand == nil || field == nil {
return
}

source := pass.FileContext.Source
if operand.StartByte() >= uint32(len(source)) || operand.EndByte() > uint32(len(source)) {
return
}
if field.StartByte() >= uint32(len(source)) || field.EndByte() > uint32(len(source)) {
return
}

operandContent := string(source[operand.StartByte():operand.EndByte()])
fieldContent := string(source[field.StartByte():field.EndByte()])

if operandContent == "bcrypt" && fieldContent == "GenerateFromPassword" {
arguments := node.ChildByFieldName("arguments")
if arguments == nil || arguments.Type() != "argument_list" || arguments.NamedChildCount() < 2 {
return
}

// Cost is the 2nd argument (index 1)
costNode := arguments.NamedChild(1)
if costNode == nil || costNode.StartByte() >= uint32(len(source)) || costNode.EndByte() > uint32(len(source)) {
return
}

costContent := string(source[costNode.StartByte():costNode.EndByte()])
cost, err := strconv.ParseInt(costContent, 10, 64)
if err != nil {
return
}

if cost < 10 {
pass.Report(pass, node, "bcrypt: Cost factor should be at least 10 (detected "+costContent+")")
}
}
})
return nil, nil
}
20 changes: 20 additions & 0 deletions checkers/golang/weak_bcrypt_cost.test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package golang

// Mock implementations to test analyzer detection
type bcryptMock struct{}

func (b *bcryptMock) GenerateFromPassword(password []byte, cost int) ([]byte, error) {
return nil, nil
}

func testBcryptCost() {
var (
bcrypt bcryptMock
)

// <expect-error>
bcrypt.GenerateFromPassword([]byte("password"), 8)

// <no-error>
bcrypt.GenerateFromPassword([]byte("password"), 12)
}

0 comments on commit 626c1f5

Please sign in to comment.