From 1d8f76dc10a12eb75faae2ab0b4981506ec6ee11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Fabianski?= Date: Mon, 9 Oct 2023 14:32:07 +0200 Subject: [PATCH] feat(golang): add initial implementation with logger rule --- .envrc.example | 1 + .../commands/process/settings/settings.go | 2 + .../golang/.snapshots/TestScope--scope.yml | 350 ++++++++ .../flow/TestFlow--different-line.yml | 138 ++++ .../.snapshots/flow/TestFlow--same-line.yml | 104 +++ .../languages/golang/analyzer/analyzer.go | 154 ++-- .../.snapshots/TestGoObjects-object_class | 414 ++++++++++ .../.snapshots/TestGoObjects-object_no_class | 379 +++++++++ .../detectors/.snapshots/TestGoString-string | 777 ++++++++++++++++++ .../golang/detectors/object/object.go | 183 ++--- .../golang/detectors/object/projection.go | 80 +- .../golang/detectors/string/string.go | 23 +- .../golang/detectors/testdata/class.go | 20 +- .../golang/detectors/testdata/no_class.go | 12 +- .../golang/detectors/testdata/string.go | 29 +- internal/languages/golang/golang.go | 2 + internal/languages/golang/golang_test.go | 4 +- internal/languages/golang/pattern/pattern.go | 88 +- internal/languages/golang/testdata/logger.yml | 13 +- .../languages/golang/testdata/scope/scope.go | 59 +- .../languages/golang/testdata/scope_rule.yml | 14 +- .../testdata/testcases/flow/different-line.go | 35 +- .../testdata/testcases/flow/same-line.go | 24 +- 23 files changed, 2533 insertions(+), 372 deletions(-) create mode 100644 internal/languages/golang/.snapshots/TestScope--scope.yml create mode 100644 internal/languages/golang/.snapshots/flow/TestFlow--different-line.yml create mode 100644 internal/languages/golang/.snapshots/flow/TestFlow--same-line.yml create mode 100644 internal/languages/golang/detectors/.snapshots/TestGoObjects-object_class create mode 100644 internal/languages/golang/detectors/.snapshots/TestGoObjects-object_no_class create mode 100644 internal/languages/golang/detectors/.snapshots/TestGoString-string diff --git a/.envrc.example b/.envrc.example index 82e20d77c..68e4c35d6 100644 --- a/.envrc.example +++ b/.envrc.example @@ -12,3 +12,4 @@ export BEARER_EXTERNAL_RULE_DIR=$PWD/../bearer-rules/rules export BEARER_FORCE=true export BEARER_PHP_ENABLED=true export BEARER_GOLANG_ENABLED=true +export BEARER_IGNORE_GIT=true diff --git a/internal/commands/process/settings/settings.go b/internal/commands/process/settings/settings.go index d9fc4c475..972be0062 100644 --- a/internal/commands/process/settings/settings.go +++ b/internal/commands/process/settings/settings.go @@ -293,6 +293,8 @@ func (rule *Rule) Language() string { return "Ruby" case "sql": return "SQL" + case "go": + return "Go" case "php": return "PHP" default: diff --git a/internal/languages/golang/.snapshots/TestScope--scope.yml b/internal/languages/golang/.snapshots/TestScope--scope.yml new file mode 100644 index 000000000..91c1dcdc1 --- /dev/null +++ b/internal/languages/golang/.snapshots/TestScope--scope.yml @@ -0,0 +1,350 @@ +high: + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 15 + full_filename: scope.go + filename: scope.go + source: + location: + start: 15 + end: 15 + column: + start: 2 + end: 40 + sink: + location: + start: 15 + end: 15 + column: + start: 2 + end: 40 + content: scopeCursor(request.FormValue("oops")) + parent_line_number: 15 + snippet: scopeCursor(request.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_0 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_0 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 18 + full_filename: scope.go + filename: scope.go + source: + location: + start: 18 + end: 18 + column: + start: 2 + end: 40 + sink: + location: + start: 18 + end: 18 + column: + start: 2 + end: 40 + content: scopeNested(request.FormValue("oops")) + parent_line_number: 18 + snippet: scopeNested(request.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_1 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_1 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 19 + full_filename: scope.go + filename: scope.go + source: + location: + start: 19 + end: 19 + column: + start: 2 + end: 44 + sink: + location: + start: 19 + end: 19 + column: + start: 2 + end: 44 + content: scopeNested(x + request.FormValue("oops")) + parent_line_number: 19 + snippet: scopeNested(x + request.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_2 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_2 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 20 + full_filename: scope.go + filename: scope.go + source: + location: + start: 20 + end: 20 + column: + start: 2 + end: 43 + sink: + location: + start: 20 + end: 20 + column: + start: 2 + end: 43 + content: scopeNested(y[request.FormValue("oops")]) + parent_line_number: 20 + snippet: scopeNested(y[request.FormValue("oops")]) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_3 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_3 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 22 + full_filename: scope.go + filename: scope.go + source: + location: + start: 22 + end: 22 + column: + start: 2 + end: 40 + sink: + location: + start: 22 + end: 22 + column: + start: 2 + end: 40 + content: scopeResult(request.FormValue("oops")) + parent_line_number: 22 + snippet: scopeResult(request.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_4 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_4 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 23 + full_filename: scope.go + filename: scope.go + source: + location: + start: 23 + end: 23 + column: + start: 2 + end: 44 + sink: + location: + start: 23 + end: 23 + column: + start: 2 + end: 44 + content: scopeResult(x + request.FormValue("oops")) + parent_line_number: 23 + snippet: scopeResult(x + request.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_5 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_5 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 32 + full_filename: scope.go + filename: scope.go + source: + location: + start: 32 + end: 32 + column: + start: 2 + end: 36 + sink: + location: + start: 32 + end: 32 + column: + start: 2 + end: 36 + content: scopeCursor(req.FormValue("oops")) + parent_line_number: 32 + snippet: scopeCursor(req.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_6 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_6 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 35 + full_filename: scope.go + filename: scope.go + source: + location: + start: 35 + end: 35 + column: + start: 2 + end: 36 + sink: + location: + start: 35 + end: 35 + column: + start: 2 + end: 36 + content: scopeNested(req.FormValue("oops")) + parent_line_number: 35 + snippet: scopeNested(req.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_7 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_7 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 36 + full_filename: scope.go + filename: scope.go + source: + location: + start: 36 + end: 36 + column: + start: 2 + end: 40 + sink: + location: + start: 36 + end: 36 + column: + start: 2 + end: 40 + content: scopeNested(x + req.FormValue("oops")) + parent_line_number: 36 + snippet: scopeNested(x + req.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_8 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_8 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 37 + full_filename: scope.go + filename: scope.go + source: + location: + start: 37 + end: 37 + column: + start: 2 + end: 39 + sink: + location: + start: 37 + end: 37 + column: + start: 2 + end: 39 + content: scopeNested(y[req.FormValue("oops")]) + parent_line_number: 37 + snippet: scopeNested(y[req.FormValue("oops")]) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_9 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_9 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 39 + full_filename: scope.go + filename: scope.go + source: + location: + start: 39 + end: 39 + column: + start: 2 + end: 36 + sink: + location: + start: 39 + end: 39 + column: + start: 2 + end: 36 + content: scopeResult(req.FormValue("oops")) + parent_line_number: 39 + snippet: scopeResult(req.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_10 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_10 + - rule: + cwe_ids: + - "42" + id: scope_test + title: Test detection filter scopes + description: Test detection filter scopes + documentation_url: "" + line_number: 40 + full_filename: scope.go + filename: scope.go + source: + location: + start: 40 + end: 40 + column: + start: 2 + end: 40 + sink: + location: + start: 40 + end: 40 + column: + start: 2 + end: 40 + content: scopeResult(x + req.FormValue("oops")) + parent_line_number: 40 + snippet: scopeResult(x + req.FormValue("oops")) + fingerprint: c87422d3d7e0f39d979f1dd26df088d6_11 + old_fingerprint: c87422d3d7e0f39d979f1dd26df088d6_11 + diff --git a/internal/languages/golang/.snapshots/flow/TestFlow--different-line.yml b/internal/languages/golang/.snapshots/flow/TestFlow--different-line.yml new file mode 100644 index 000000000..9b7db3427 --- /dev/null +++ b/internal/languages/golang/.snapshots/flow/TestFlow--different-line.yml @@ -0,0 +1,138 @@ +high: + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 20 + full_filename: different-line.go + filename: different-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 20 + end: 20 + column: + start: 3 + end: 7 + sink: + location: + start: 31 + end: 31 + column: + start: 2 + end: 23 + content: log.Error().Msg(user) + parent_line_number: 31 + snippet: log.Error().Msg(user) + fingerprint: f8cb961f0fc2f87d026bf9f5db408736_0 + old_fingerprint: f8cb961f0fc2f87d026bf9f5db408736_0 + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 21 + full_filename: different-line.go + filename: different-line.go + data_type: + category_uuid: 94007e1e-57d8-43e8-90f2-246236dc5dde + name: Gender + category_groups: + - PII + - Personal Data + source: + location: + start: 21 + end: 21 + column: + start: 3 + end: 9 + sink: + location: + start: 31 + end: 31 + column: + start: 2 + end: 23 + content: log.Error().Msg(user) + parent_line_number: 31 + snippet: log.Error().Msg(user) + fingerprint: f8cb961f0fc2f87d026bf9f5db408736_1 + old_fingerprint: f8cb961f0fc2f87d026bf9f5db408736_1 + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 24 + full_filename: different-line.go + filename: different-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 24 + end: 24 + column: + start: 10 + end: 19 + sink: + location: + start: 29 + end: 29 + column: + start: 2 + end: 23 + content: log.Error().Msg(name) + parent_line_number: 29 + snippet: log.Error().Msg(name) + fingerprint: f8cb961f0fc2f87d026bf9f5db408736_2 + old_fingerprint: f8cb961f0fc2f87d026bf9f5db408736_2 + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 26 + full_filename: different-line.go + filename: different-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 26 + end: 26 + column: + start: 14 + end: 27 + sink: + location: + start: 30 + end: 30 + column: + start: 2 + end: 24 + content: log.Error().Msg(other) + parent_line_number: 30 + snippet: log.Error().Msg(other) + fingerprint: f8cb961f0fc2f87d026bf9f5db408736_3 + old_fingerprint: f8cb961f0fc2f87d026bf9f5db408736_3 + diff --git a/internal/languages/golang/.snapshots/flow/TestFlow--same-line.yml b/internal/languages/golang/.snapshots/flow/TestFlow--same-line.yml new file mode 100644 index 000000000..d665ed74f --- /dev/null +++ b/internal/languages/golang/.snapshots/flow/TestFlow--same-line.yml @@ -0,0 +1,104 @@ +high: + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 19 + full_filename: same-line.go + filename: same-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 19 + end: 19 + column: + start: 18 + end: 27 + sink: + location: + start: 19 + end: 19 + column: + start: 2 + end: 28 + content: log.Error().Msg(user.Name) + parent_line_number: 19 + snippet: log.Error().Msg(user.Name) + fingerprint: 03b8fc38b73518ac9530e238e3db6896_0 + old_fingerprint: 03b8fc38b73518ac9530e238e3db6896_0 + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 20 + full_filename: same-line.go + filename: same-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 20 + end: 20 + column: + start: 18 + end: 31 + sink: + location: + start: 20 + end: 20 + column: + start: 2 + end: 34 + content: log.Error().Msg(user.FullName()) + parent_line_number: 20 + snippet: log.Error().Msg(user.FullName()) + fingerprint: 03b8fc38b73518ac9530e238e3db6896_1 + old_fingerprint: 03b8fc38b73518ac9530e238e3db6896_1 + - rule: + cwe_ids: [] + id: rule_logger_test + title: "" + description: "" + documentation_url: "" + line_number: 21 + full_filename: same-line.go + filename: same-line.go + data_type: + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e + name: Fullname + category_groups: + - PII + - Personal Data + source: + location: + start: 21 + end: 21 + column: + start: 35 + end: 48 + sink: + location: + start: 21 + end: 21 + column: + start: 2 + end: 51 + content: log.Error().Msgf("user info %s", user.FullName()) + parent_line_number: 21 + snippet: log.Error().Msgf("user info %s", user.FullName()) + fingerprint: 03b8fc38b73518ac9530e238e3db6896_2 + old_fingerprint: 03b8fc38b73518ac9530e238e3db6896_2 + diff --git a/internal/languages/golang/analyzer/analyzer.go b/internal/languages/golang/analyzer/analyzer.go index 6669c5056..e0b802415 100644 --- a/internal/languages/golang/analyzer/analyzer.go +++ b/internal/languages/golang/analyzer/analyzer.go @@ -2,6 +2,7 @@ package analyzer import ( sitter "github.com/smacker/go-tree-sitter" + "golang.org/x/exp/slices" "github.com/bearer/bearer/internal/scanner/ast/tree" "github.com/bearer/bearer/internal/scanner/language" @@ -21,50 +22,33 @@ func New(builder *tree.Builder) language.Analyzer { func (analyzer *analyzer) Analyze(node *sitter.Node, visitChildren func() error) error { switch node.Type() { - case "declaration_list", "class_declaration", "anonymous_function_creation_expression", "for_statement", "block", "method_declaration": + case "for_statement", "block", "method_declaration", "function_declaration": return analyzer.withScope(language.NewScope(analyzer.scope), func() error { return visitChildren() }) - case "augmented_assignment_expression": - return analyzer.analyzeAugmentedAssignment(node, visitChildren) + case "short_var_declaration": + return analyzer.analyzeShortVarDeclaration(node, visitChildren) + case "var_spec": + return analyzer.analyzeVarSpecDeclaration(node, visitChildren) case "assignment_expression": return analyzer.analyzeAssignment(node, visitChildren) - case "parenthesized_expression": - return analyzer.analyzeParentheses(node, visitChildren) - case "conditional_expression": - return analyzer.analyzeConditional(node, visitChildren) - case "function_call_expression", "member_call_expression": - return analyzer.analyzeMethodInvocation(node, visitChildren) - case "member_access_expression": - return analyzer.analyzeFieldAccess(node, visitChildren) - case "simple_parameter", "variadic_parameter": + case "call_expression": + return analyzer.analyzeCallExpression(node, visitChildren) + case "selector_expression": + return analyzer.analyzeSelectorExpression(node, visitChildren) + case "parameter_declaration": return analyzer.analyzeParameter(node, visitChildren) - case "switch_statement": + case "expression_switch_statement": return analyzer.analyzeSwitch(node, visitChildren) - case "switch_block": + case "expression_case", "default_case": return analyzer.analyzeGenericConstruct(node, visitChildren) - case "switch_label": - return visitChildren() - case "dynamic_variable_name": - return analyzer.analyzeDynamicVariableName(node, visitChildren) - case "binary_expression", - "unary_op_expression", - "argument", - "encapsed_string", - "sequence_expression", - "array_element_initializer", - "formal_parameters", - "include_expression", - "include_once_expression", - "require_expression", - "require_once_expression": + case "argument_list": return analyzer.analyzeGenericOperation(node, visitChildren) - case "while_statement", "do_statement", "if_statement", "expression_statement", "compound_statement": // statements don't have results + case "return_statement", "go_statement", "defer_statement", "if_statement": // statements don't have results return visitChildren() - case "variable_name": + case "identifier": return visitChildren() - case "match_expression": - analyzer.builder.Dataflow(node, analyzer.builder.ChildrenExcept(node, node.ChildByFieldName("condition"))...) + case "index_expression": return visitChildren() default: analyzer.builder.Dataflow(node, analyzer.builder.ChildrenFor(node)...) @@ -72,73 +56,65 @@ func (analyzer *analyzer) Analyze(node *sitter.Node, visitChildren func() error) } } -// $foo = a +// foo = a +// foo += a func (analyzer *analyzer) analyzeAssignment(node *sitter.Node, visitChildren func() error) error { - left := node.ChildByFieldName("left") - right := node.ChildByFieldName("right") - analyzer.builder.Alias(node, right) + left := node.ChildByFieldName("left").Child(0) + right := node.ChildByFieldName("right").Child(0) + + if analyzer.builder.ContentFor(node.Child(1)) == "=" { + analyzer.builder.Alias(node, right) + } else { // += + analyzer.builder.Dataflow(node, left, right) + analyzer.lookupVariable(left) + } + analyzer.lookupVariable(right) err := visitChildren() - if left.Type() == "variable_name" { - analyzer.scope.Assign(analyzer.builder.ContentFor(left), node) - } + analyzer.scope.Assign(analyzer.builder.ContentFor(left), node) return err } -// $foo .= a -func (analyzer *analyzer) analyzeAugmentedAssignment(node *sitter.Node, visitChildren func() error) error { +// foo, err := a +func (analyzer *analyzer) analyzeShortVarDeclaration(node *sitter.Node, visitChildren func() error) error { left := node.ChildByFieldName("left") right := node.ChildByFieldName("right") - analyzer.builder.Dataflow(node, left, right) - analyzer.lookupVariable(left) - analyzer.lookupVariable(right) - - err := visitChildren() - if left.Type() == "variable_name" { - analyzer.scope.Assign(analyzer.builder.ContentFor(left), node) + for _, child := range analyzer.builder.ChildrenFor(left) { + if !slices.Contains([]string{"_", "err"}, analyzer.builder.ContentFor(child)) { + analyzer.scope.Declare(analyzer.builder.ContentFor(child), child) + analyzer.scope.Assign(analyzer.builder.ContentFor(child), node) + } } - return err -} + for _, child := range analyzer.builder.ChildrenFor(right) { + analyzer.builder.Alias(node, child) + analyzer.lookupVariable(child) + } -func (analyzer *analyzer) analyzeParentheses(node *sitter.Node, visitChildren func() error) error { - analyzer.builder.Alias(node, node.NamedChild(0)) - analyzer.lookupVariable(node.NamedChild(0)) err := visitChildren() return err } -// a ? x : y -// a ?: x -func (analyzer *analyzer) analyzeConditional(node *sitter.Node, visitChildren func() error) error { - condition := node.ChildByFieldName("condition") - consequence := node.ChildByFieldName("body") - alternative := node.ChildByFieldName("alternative") - - analyzer.lookupVariable(condition) - analyzer.lookupVariable(consequence) - analyzer.lookupVariable(alternative) - - if consequence != nil { - analyzer.builder.Alias(node, consequence, alternative) - } else { - analyzer.builder.Alias(node, condition, alternative) +// var a, b string +func (analyzer *analyzer) analyzeVarSpecDeclaration(node *sitter.Node, visitChildren func() error) error { + for _, child := range analyzer.builder.ChildrenFor(node) { + if child.Type() == "identifier" { + analyzer.scope.Declare(analyzer.builder.ContentFor(child), child) + } } - return visitChildren() -} + err := visitChildren() -// foo(1, 2); -// foo->bar(1, 2); -func (analyzer *analyzer) analyzeMethodInvocation(node *sitter.Node, visitChildren func() error) error { - analyzer.lookupVariable(node.ChildByFieldName("object")) // method - analyzer.lookupVariable(node.ChildByFieldName("function")) // function + return err +} +// foo(1, 2) +func (analyzer *analyzer) analyzeCallExpression(node *sitter.Node, visitChildren func() error) error { if arguments := node.ChildByFieldName("arguments"); arguments != nil { analyzer.builder.Dataflow(node, arguments) } @@ -146,35 +122,27 @@ func (analyzer *analyzer) analyzeMethodInvocation(node *sitter.Node, visitChildr return visitChildren() } -// foo->bar -func (analyzer *analyzer) analyzeFieldAccess(node *sitter.Node, visitChildren func() error) error { - analyzer.lookupVariable(node.ChildByFieldName("object")) +// foo.bar +func (analyzer *analyzer) analyzeSelectorExpression(node *sitter.Node, visitChildren func() error) error { + analyzer.lookupVariable(node.ChildByFieldName("operand")) return visitChildren() } // method parameter declaration // -// fn(bool $a) => $a; -// fn($x = 42) => $x; -// fn($x, ...$rest) => $rest; +// fn(a string) func (analyzer *analyzer) analyzeParameter(node *sitter.Node, visitChildren func() error) error { name := node.ChildByFieldName("name") - analyzer.builder.Alias(node, name) - analyzer.scope.Declare(analyzer.builder.ContentFor(name), name) + if name != nil { + analyzer.builder.Alias(node, name) + analyzer.scope.Declare(analyzer.builder.ContentFor(name), name) + } return visitChildren() } func (analyzer *analyzer) analyzeSwitch(node *sitter.Node, visitChildren func() error) error { - analyzer.builder.Alias(node, node.ChildByFieldName("body")) - - return visitChildren() -} - -func (analyzer *analyzer) analyzeDynamicVariableName(node *sitter.Node, visitChildren func() error) error { - analyzer.lookupVariable(node.NamedChild(0)) - return visitChildren() } @@ -208,7 +176,7 @@ func (analyzer *analyzer) withScope(newScope *language.Scope, body func() error) } func (analyzer *analyzer) lookupVariable(node *sitter.Node) { - if node == nil || node.Type() != "variable_name" { + if node == nil || node.Type() != "identifier" { return } diff --git a/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_class b/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_class new file mode 100644 index 000000000..39e222917 --- /dev/null +++ b/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_class @@ -0,0 +1,414 @@ +type: source_file +id: 0 +range: 1:1 - 15:1 +dataflow_sources: + - 1 + - 4 + - 5 + - 11 + - 12 + - 37 + - 38 + - 66 +children: + - type: package_clause + id: 1 + range: 1:1 - 1:12 + dataflow_sources: + - 2 + - 3 + children: + - type: '"package"' + id: 2 + range: 1:1 - 1:8 + - type: package_identifier + id: 3 + range: 1:9 - 1:12 + content: foo + - type: |- + " + " + id: 4 + range: 1:12 - 3:1 + - type: import_declaration + id: 5 + range: 3:1 - 3:13 + dataflow_sources: + - 6 + - 7 + children: + - type: '"import"' + id: 6 + range: 3:1 - 3:7 + - type: import_spec + id: 7 + range: 3:8 - 3:13 + dataflow_sources: + - 8 + children: + - type: interpreted_string_literal + id: 8 + range: 3:8 - 3:13 + dataflow_sources: + - 9 + - 10 + children: + - type: '"""' + id: 9 + range: 3:8 - 3:9 + - type: '"""' + id: 10 + range: 3:12 - 3:13 + - type: |- + " + " + id: 11 + range: 3:13 - 5:1 + - type: type_declaration + id: 12 + range: 5:1 - 10:2 + dataflow_sources: + - 13 + - 14 + children: + - type: '"type"' + id: 13 + range: 5:1 - 5:5 + - type: type_spec + id: 14 + range: 5:6 - 10:2 + dataflow_sources: + - 15 + - 16 + children: + - type: type_identifier + id: 15 + range: 5:6 - 5:10 + content: User + - type: struct_type + id: 16 + range: 5:11 - 10:2 + dataflow_sources: + - 17 + - 18 + children: + - type: '"struct"' + id: 17 + range: 5:11 - 5:17 + - type: field_declaration_list + id: 18 + range: 5:18 - 10:2 + dataflow_sources: + - 19 + - 20 + - 23 + - 24 + - 27 + - 28 + - 31 + - 32 + - 35 + - 36 + children: + - type: '"{"' + id: 19 + range: 5:18 - 5:19 + - type: field_declaration + id: 20 + range: 6:2 - 6:18 + dataflow_sources: + - 21 + - 22 + children: + - type: field_identifier + id: 21 + range: 6:2 - 6:11 + content: firstName + - type: type_identifier + id: 22 + range: 6:12 - 6:18 + content: string + - type: |- + " + " + id: 23 + range: 6:18 - 7:1 + - type: field_declaration + id: 24 + range: 7:2 - 7:18 + dataflow_sources: + - 25 + - 26 + children: + - type: field_identifier + id: 25 + range: 7:2 - 7:10 + content: lastName + - type: type_identifier + id: 26 + range: 7:12 - 7:18 + content: string + - type: |- + " + " + id: 27 + range: 7:18 - 8:1 + - type: field_declaration + id: 28 + range: 8:2 - 8:18 + dataflow_sources: + - 29 + - 30 + children: + - type: field_identifier + id: 29 + range: 8:2 - 8:7 + content: email + - type: type_identifier + id: 30 + range: 8:12 - 8:18 + content: string + - type: |- + " + " + id: 31 + range: 8:18 - 9:1 + - type: field_declaration + id: 32 + range: 9:2 - 9:18 + dataflow_sources: + - 33 + - 34 + children: + - type: field_identifier + id: 33 + range: 9:2 - 9:6 + content: uuid + - type: type_identifier + id: 34 + range: 9:12 - 9:18 + content: string + - type: |- + " + " + id: 35 + range: 9:18 - 10:1 + - type: '"}"' + id: 36 + range: 10:1 - 10:2 + - type: |- + " + " + id: 37 + range: 10:2 - 12:1 + - type: method_declaration + id: 38 + range: 12:1 - 14:2 + children: + - type: '"func"' + id: 39 + range: 12:1 - 12:5 + - type: parameter_list + id: 40 + range: 12:6 - 12:14 + dataflow_sources: + - 41 + - 42 + - 45 + children: + - type: '"("' + id: 41 + range: 12:6 - 12:7 + - type: parameter_declaration + id: 42 + range: 12:7 - 12:13 + alias_of: + - 43 + children: + - type: identifier + id: 43 + range: 12:7 - 12:8 + content: x + - type: type_identifier + id: 44 + range: 12:9 - 12:13 + content: User + - type: '")"' + id: 45 + range: 12:13 - 12:14 + - type: field_identifier + id: 46 + range: 12:15 - 12:19 + content: Name + - type: parameter_list + id: 47 + range: 12:19 - 12:21 + dataflow_sources: + - 48 + - 49 + children: + - type: '"("' + id: 48 + range: 12:19 - 12:20 + - type: '")"' + id: 49 + range: 12:20 - 12:21 + - type: block + id: 50 + range: 12:22 - 14:2 + children: + - type: '"{"' + id: 51 + range: 12:22 - 12:23 + - type: call_expression + id: 52 + range: 13:2 - 13:26 + dataflow_sources: + - 57 + children: + - type: selector_expression + id: 53 + range: 13:2 - 13:13 + queries: + - 1 + children: + - type: identifier + id: 54 + range: 13:2 - 13:5 + content: fmt + - type: '"."' + id: 55 + range: 13:5 - 13:6 + - type: field_identifier + id: 56 + range: 13:6 - 13:13 + content: Println + - type: argument_list + id: 57 + range: 13:13 - 13:26 + dataflow_sources: + - 58 + - 59 + - 63 + children: + - type: '"("' + id: 58 + range: 13:13 - 13:14 + - type: selector_expression + id: 59 + range: 13:14 - 13:25 + queries: + - 1 + children: + - type: identifier + id: 60 + range: 13:14 - 13:15 + content: x + alias_of: + - 43 + - type: '"."' + id: 61 + range: 13:15 - 13:16 + - type: field_identifier + id: 62 + range: 13:16 - 13:25 + content: firstName + - type: '")"' + id: 63 + range: 13:25 - 13:26 + - type: |- + " + " + id: 64 + range: 13:26 - 14:1 + - type: '"}"' + id: 65 + range: 14:1 - 14:2 + - type: |- + " + " + id: 66 + range: 14:2 - 15:1 + +- node: 52 + content: fmt.Println(x.firstName) + data: + properties: + - name: fmt + node: null + object: + ruleid: object + matchnode: + id: 53 + typeid: 30 + contentstart: + byte: 145 + line: 13 + column: 2 + contentend: + byte: 156 + line: 13 + column: 13 + executingdetectors: [] + data: + properties: + - name: Println + node: null + object: null + isvirtual: true + isvirtual: true +- node: 53 + content: fmt.Println + data: + properties: + - name: fmt + node: null + object: + ruleid: object + matchnode: + id: 53 + typeid: 30 + contentstart: + byte: 145 + line: 13 + column: 2 + contentend: + byte: 156 + line: 13 + column: 13 + executingdetectors: [] + data: + properties: + - name: Println + node: null + object: null + isvirtual: true + isvirtual: true +- node: 59 + content: x.firstName + data: + properties: + - name: x + node: null + object: + ruleid: object + matchnode: + id: 59 + typeid: 30 + contentstart: + byte: 157 + line: 13 + column: 14 + contentend: + byte: 168 + line: 13 + column: 25 + executingdetectors: [] + data: + properties: + - name: firstName + node: null + object: null + isvirtual: true + isvirtual: true + diff --git a/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_no_class b/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_no_class new file mode 100644 index 000000000..44540cec3 --- /dev/null +++ b/internal/languages/golang/detectors/.snapshots/TestGoObjects-object_no_class @@ -0,0 +1,379 @@ +type: source_file +id: 0 +range: 1:1 - 12:1 +dataflow_sources: + - 1 + - 4 + - 5 + - 11 + - 12 + - 52 +children: + - type: package_clause + id: 1 + range: 1:1 - 1:12 + dataflow_sources: + - 2 + - 3 + children: + - type: '"package"' + id: 2 + range: 1:1 - 1:8 + - type: package_identifier + id: 3 + range: 1:9 - 1:12 + content: foo + - type: |- + " + " + id: 4 + range: 1:12 - 3:1 + - type: import_declaration + id: 5 + range: 3:1 - 3:13 + dataflow_sources: + - 6 + - 7 + children: + - type: '"import"' + id: 6 + range: 3:1 - 3:7 + - type: import_spec + id: 7 + range: 3:8 - 3:13 + dataflow_sources: + - 8 + children: + - type: interpreted_string_literal + id: 8 + range: 3:8 - 3:13 + dataflow_sources: + - 9 + - 10 + children: + - type: '"""' + id: 9 + range: 3:8 - 3:9 + - type: '"""' + id: 10 + range: 3:12 - 3:13 + - type: |- + " + " + id: 11 + range: 3:13 - 5:1 + - type: function_declaration + id: 12 + range: 5:1 - 11:2 + children: + - type: '"func"' + id: 13 + range: 5:1 - 5:5 + - type: identifier + id: 14 + range: 5:6 - 5:10 + content: main + - type: parameter_list + id: 15 + range: 5:10 - 5:12 + dataflow_sources: + - 16 + - 17 + children: + - type: '"("' + id: 16 + range: 5:10 - 5:11 + - type: '")"' + id: 17 + range: 5:11 - 5:12 + - type: block + id: 18 + range: 5:13 - 11:2 + children: + - type: '"{"' + id: 19 + range: 5:13 - 5:14 + - type: short_var_declaration + id: 20 + range: 6:2 - 8:3 + alias_of: + - 25 + queries: + - 0 + children: + - type: expression_list + id: 21 + range: 6:2 - 6:6 + dataflow_sources: + - 22 + children: + - type: identifier + id: 22 + range: 6:2 - 6:6 + content: user + - type: '":="' + id: 23 + range: 6:7 - 6:9 + - type: expression_list + id: 24 + range: 6:10 - 8:3 + dataflow_sources: + - 25 + children: + - type: composite_literal + id: 25 + range: 6:10 - 8:3 + dataflow_sources: + - 26 + - 27 + queries: + - 2 + children: + - type: type_identifier + id: 26 + range: 6:10 - 6:14 + content: User + - type: literal_value + id: 27 + range: 6:14 - 8:3 + dataflow_sources: + - 28 + - 29 + - 35 + - 36 + children: + - type: '"{"' + id: 28 + range: 6:14 - 6:15 + - type: keyed_element + id: 29 + range: 7:3 - 7:23 + dataflow_sources: + - 30 + - 31 + - 32 + children: + - type: field_identifier + id: 30 + range: 7:3 - 7:8 + content: email + - type: '":"' + id: 31 + range: 7:8 - 7:9 + - type: interpreted_string_literal + id: 32 + range: 7:10 - 7:23 + dataflow_sources: + - 33 + - 34 + children: + - type: '"""' + id: 33 + range: 7:10 - 7:11 + - type: '"""' + id: 34 + range: 7:22 - 7:23 + - type: '","' + id: 35 + range: 7:23 - 7:24 + - type: '"}"' + id: 36 + range: 8:2 - 8:3 + - type: |- + " + " + id: 37 + range: 8:3 - 10:1 + - type: call_expression + id: 38 + range: 10:2 - 10:25 + dataflow_sources: + - 43 + children: + - type: selector_expression + id: 39 + range: 10:2 - 10:13 + queries: + - 1 + children: + - type: identifier + id: 40 + range: 10:2 - 10:5 + content: fmt + - type: '"."' + id: 41 + range: 10:5 - 10:6 + - type: field_identifier + id: 42 + range: 10:6 - 10:13 + content: Println + - type: argument_list + id: 43 + range: 10:13 - 10:25 + dataflow_sources: + - 44 + - 45 + - 49 + children: + - type: '"("' + id: 44 + range: 10:13 - 10:14 + - type: selector_expression + id: 45 + range: 10:14 - 10:24 + queries: + - 1 + children: + - type: identifier + id: 46 + range: 10:14 - 10:18 + content: user + alias_of: + - 20 + - type: '"."' + id: 47 + range: 10:18 - 10:19 + - type: field_identifier + id: 48 + range: 10:19 - 10:24 + content: email + - type: '")"' + id: 49 + range: 10:24 - 10:25 + - type: |- + " + " + id: 50 + range: 10:25 - 11:1 + - type: '"}"' + id: 51 + range: 11:1 - 11:2 + - type: |- + " + " + id: 52 + range: 11:2 - 12:1 + +- node: 38 + content: fmt.Println(user.email) + data: + properties: + - name: fmt + node: null + object: + ruleid: object + matchnode: + id: 39 + typeid: 30 + contentstart: + byte: 85 + line: 10 + column: 2 + contentend: + byte: 96 + line: 10 + column: 13 + executingdetectors: [] + data: + properties: + - name: Println + node: null + object: null + isvirtual: true + isvirtual: true +- node: 39 + content: fmt.Println + data: + properties: + - name: fmt + node: null + object: + ruleid: object + matchnode: + id: 39 + typeid: 30 + contentstart: + byte: 85 + line: 10 + column: 2 + contentend: + byte: 96 + line: 10 + column: 13 + executingdetectors: [] + data: + properties: + - name: Println + node: null + object: null + isvirtual: true + isvirtual: true +- node: 25 + content: |- + User{ + email: "foo@bar.com", + } + data: + properties: + - name: User + node: null + object: + ruleid: object + matchnode: + id: 25 + typeid: 21 + contentstart: + byte: 50 + line: 6 + column: 10 + contentend: + byte: 82 + line: 8 + column: 3 + executingdetectors: [] + data: + properties: + - name: email + node: + id: 30 + typeid: 25 + contentstart: + byte: 58 + line: 7 + column: 3 + contentend: + byte: 63 + line: 7 + column: 8 + executingdetectors: [] + object: null + isvirtual: false + isvirtual: false +- node: 45 + content: user.email + data: + properties: + - name: user + node: null + object: + ruleid: object + matchnode: + id: 45 + typeid: 30 + contentstart: + byte: 97 + line: 10 + column: 14 + contentend: + byte: 107 + line: 10 + column: 24 + executingdetectors: [] + data: + properties: + - name: email + node: null + object: null + isvirtual: true + isvirtual: true + diff --git a/internal/languages/golang/detectors/.snapshots/TestGoString-string b/internal/languages/golang/detectors/.snapshots/TestGoString-string new file mode 100644 index 000000000..4883861aa --- /dev/null +++ b/internal/languages/golang/detectors/.snapshots/TestGoString-string @@ -0,0 +1,777 @@ +type: source_file +id: 0 +range: 1:1 - 23:1 +dataflow_sources: + - 1 + - 4 + - 5 + - 20 + - 21 + - 30 + - 31 + - 147 +children: + - type: package_clause + id: 1 + range: 1:1 - 1:13 + dataflow_sources: + - 2 + - 3 + children: + - type: '"package"' + id: 2 + range: 1:1 - 1:8 + - type: package_identifier + id: 3 + range: 1:9 - 1:13 + content: main + - type: |- + " + " + id: 4 + range: 1:13 - 3:1 + - type: import_declaration + id: 5 + range: 3:1 - 6:2 + dataflow_sources: + - 6 + - 7 + children: + - type: '"import"' + id: 6 + range: 3:1 - 3:7 + - type: import_spec_list + id: 7 + range: 3:8 - 6:2 + dataflow_sources: + - 8 + - 9 + - 13 + - 14 + - 18 + - 19 + children: + - type: '"("' + id: 8 + range: 3:8 - 3:9 + - type: import_spec + id: 9 + range: 4:2 - 4:7 + dataflow_sources: + - 10 + children: + - type: interpreted_string_literal + id: 10 + range: 4:2 - 4:7 + dataflow_sources: + - 11 + - 12 + children: + - type: '"""' + id: 11 + range: 4:2 - 4:3 + - type: '"""' + id: 12 + range: 4:6 - 4:7 + - type: |- + " + " + id: 13 + range: 4:7 - 5:1 + - type: import_spec + id: 14 + range: 5:2 - 5:6 + dataflow_sources: + - 15 + children: + - type: interpreted_string_literal + id: 15 + range: 5:2 - 5:6 + dataflow_sources: + - 16 + - 17 + children: + - type: '"""' + id: 16 + range: 5:2 - 5:3 + - type: '"""' + id: 17 + range: 5:5 - 5:6 + - type: |- + " + " + id: 18 + range: 5:6 - 6:1 + - type: '")"' + id: 19 + range: 6:1 - 6:2 + - type: |- + " + " + id: 20 + range: 6:2 - 8:1 + - type: var_declaration + id: 21 + range: 8:1 - 8:29 + dataflow_sources: + - 22 + - 23 + children: + - type: '"var"' + id: 22 + range: 8:1 - 8:4 + - type: var_spec + id: 23 + range: 8:5 - 8:29 + children: + - type: identifier + id: 24 + range: 8:5 - 8:13 + content: Greeting + - type: '"="' + id: 25 + range: 8:14 - 8:15 + - type: expression_list + id: 26 + range: 8:16 - 8:29 + dataflow_sources: + - 27 + children: + - type: interpreted_string_literal + id: 27 + range: 8:16 - 8:29 + dataflow_sources: + - 28 + - 29 + children: + - type: '"""' + id: 28 + range: 8:16 - 8:17 + - type: '"""' + id: 29 + range: 8:28 - 8:29 + - type: |- + " + " + id: 30 + range: 8:29 - 10:1 + - type: function_declaration + id: 31 + range: 10:1 - 22:2 + children: + - type: '"func"' + id: 32 + range: 10:1 - 10:5 + - type: identifier + id: 33 + range: 10:6 - 10:10 + content: main + - type: parameter_list + id: 34 + range: 10:10 - 10:12 + dataflow_sources: + - 35 + - 36 + children: + - type: '"("' + id: 35 + range: 10:10 - 10:11 + - type: '")"' + id: 36 + range: 10:11 - 10:12 + - type: block + id: 37 + range: 10:13 - 22:2 + children: + - type: '"{"' + id: 38 + range: 10:13 - 10:14 + - type: short_var_declaration + id: 39 + range: 11:2 - 11:21 + alias_of: + - 44 + queries: + - 0 + children: + - type: expression_list + id: 40 + range: 11:2 - 11:3 + dataflow_sources: + - 41 + children: + - type: identifier + id: 41 + range: 11:2 - 11:3 + content: s + - type: '":="' + id: 42 + range: 11:4 - 11:6 + - type: expression_list + id: 43 + range: 11:7 - 11:21 + dataflow_sources: + - 44 + children: + - type: binary_expression + id: 44 + range: 11:7 - 11:21 + dataflow_sources: + - 45 + - 46 + - 47 + children: + - type: identifier + id: 45 + range: 11:7 - 11:15 + content: Greeting + - type: '"+"' + id: 46 + range: 11:16 - 11:17 + - type: interpreted_string_literal + id: 47 + range: 11:18 - 11:21 + dataflow_sources: + - 48 + - 49 + children: + - type: '"""' + id: 48 + range: 11:18 - 11:19 + - type: '"""' + id: 49 + range: 11:20 - 11:21 + - type: |- + " + " + id: 50 + range: 11:21 - 12:1 + - type: assignment_statement + id: 51 + range: 12:2 - 12:11 + dataflow_sources: + - 52 + - 54 + - 55 + queries: + - 0 + children: + - type: expression_list + id: 52 + range: 12:2 - 12:3 + dataflow_sources: + - 53 + children: + - type: identifier + id: 53 + range: 12:2 - 12:3 + content: s + - type: '"+="' + id: 54 + range: 12:4 - 12:6 + - type: expression_list + id: 55 + range: 12:7 - 12:11 + dataflow_sources: + - 56 + children: + - type: interpreted_string_literal + id: 56 + range: 12:7 - 12:11 + dataflow_sources: + - 57 + - 58 + children: + - type: '"""' + id: 57 + range: 12:7 - 12:8 + - type: '"""' + id: 58 + range: 12:10 - 12:11 + - type: |- + " + " + id: 59 + range: 12:11 - 13:1 + - type: call_expression + id: 60 + range: 13:2 - 13:16 + dataflow_sources: + - 65 + children: + - type: selector_expression + id: 61 + range: 13:2 - 13:13 + queries: + - 1 + children: + - type: identifier + id: 62 + range: 13:2 - 13:5 + content: fmt + - type: '"."' + id: 63 + range: 13:5 - 13:6 + - type: field_identifier + id: 64 + range: 13:6 - 13:13 + content: Println + - type: argument_list + id: 65 + range: 13:13 - 13:16 + dataflow_sources: + - 66 + - 67 + - 68 + children: + - type: '"("' + id: 66 + range: 13:13 - 13:14 + - type: identifier + id: 67 + range: 13:14 - 13:15 + content: s + alias_of: + - 39 + - type: '")"' + id: 68 + range: 13:15 - 13:16 + - type: |- + " + " + id: 69 + range: 13:16 - 15:1 + - type: short_var_declaration + id: 70 + range: 15:2 - 15:14 + alias_of: + - 75 + queries: + - 0 + children: + - type: expression_list + id: 71 + range: 15:2 - 15:4 + dataflow_sources: + - 72 + children: + - type: identifier + id: 72 + range: 15:2 - 15:4 + content: s2 + - type: '":="' + id: 73 + range: 15:5 - 15:7 + - type: expression_list + id: 74 + range: 15:8 - 15:14 + dataflow_sources: + - 75 + children: + - type: interpreted_string_literal + id: 75 + range: 15:8 - 15:14 + dataflow_sources: + - 76 + - 77 + children: + - type: '"""' + id: 76 + range: 15:8 - 15:9 + - type: '"""' + id: 77 + range: 15:13 - 15:14 + - type: |- + " + " + id: 78 + range: 15:14 - 16:1 + - type: assignment_statement + id: 79 + range: 16:2 - 16:18 + dataflow_sources: + - 80 + - 82 + - 83 + queries: + - 0 + children: + - type: expression_list + id: 80 + range: 16:2 - 16:4 + dataflow_sources: + - 81 + children: + - type: identifier + id: 81 + range: 16:2 - 16:4 + content: s2 + - type: '"+="' + id: 82 + range: 16:5 - 16:7 + - type: expression_list + id: 83 + range: 16:8 - 16:18 + dataflow_sources: + - 84 + children: + - type: index_expression + id: 84 + range: 16:8 - 16:18 + children: + - type: selector_expression + id: 85 + range: 16:8 - 16:15 + queries: + - 1 + children: + - type: identifier + id: 86 + range: 16:8 - 16:10 + content: os + - type: '"."' + id: 87 + range: 16:10 - 16:11 + - type: field_identifier + id: 88 + range: 16:11 - 16:15 + content: Args + - type: '"["' + id: 89 + range: 16:15 - 16:16 + - type: int_literal + id: 90 + range: 16:16 - 16:17 + content: "0" + - type: '"]"' + id: 91 + range: 16:17 - 16:18 + - type: |- + " + " + id: 92 + range: 16:18 - 17:1 + - type: assignment_statement + id: 93 + range: 17:2 - 17:16 + dataflow_sources: + - 94 + - 96 + - 97 + queries: + - 0 + children: + - type: expression_list + id: 94 + range: 17:2 - 17:4 + dataflow_sources: + - 95 + children: + - type: identifier + id: 95 + range: 17:2 - 17:4 + content: s2 + - type: '"+="' + id: 96 + range: 17:5 - 17:7 + - type: expression_list + id: 97 + range: 17:8 - 17:16 + dataflow_sources: + - 98 + children: + - type: interpreted_string_literal + id: 98 + range: 17:8 - 17:16 + dataflow_sources: + - 99 + - 100 + children: + - type: '"""' + id: 99 + range: 17:8 - 17:9 + - type: '"""' + id: 100 + range: 17:15 - 17:16 + - type: |- + " + " + id: 101 + range: 17:16 - 18:1 + - type: call_expression + id: 102 + range: 18:2 - 18:17 + dataflow_sources: + - 107 + children: + - type: selector_expression + id: 103 + range: 18:2 - 18:13 + queries: + - 1 + children: + - type: identifier + id: 104 + range: 18:2 - 18:5 + content: fmt + - type: '"."' + id: 105 + range: 18:5 - 18:6 + - type: field_identifier + id: 106 + range: 18:6 - 18:13 + content: Println + - type: argument_list + id: 107 + range: 18:13 - 18:17 + dataflow_sources: + - 108 + - 109 + - 110 + children: + - type: '"("' + id: 108 + range: 18:13 - 18:14 + - type: identifier + id: 109 + range: 18:14 - 18:16 + content: s2 + alias_of: + - 70 + - type: '")"' + id: 110 + range: 18:16 - 18:17 + - type: |- + " + " + id: 111 + range: 18:17 - 20:1 + - type: short_var_declaration + id: 112 + range: 20:2 - 20:36 + alias_of: + - 117 + queries: + - 0 + children: + - type: expression_list + id: 113 + range: 20:2 - 20:4 + dataflow_sources: + - 114 + children: + - type: identifier + id: 114 + range: 20:2 - 20:4 + content: s3 + - type: '":="' + id: 115 + range: 20:5 - 20:7 + - type: expression_list + id: 116 + range: 20:8 - 20:36 + dataflow_sources: + - 117 + children: + - type: binary_expression + id: 117 + range: 20:8 - 20:36 + dataflow_sources: + - 118 + - 131 + - 132 + children: + - type: binary_expression + id: 118 + range: 20:8 - 20:27 + dataflow_sources: + - 119 + - 122 + - 123 + children: + - type: interpreted_string_literal + id: 119 + range: 20:8 - 20:14 + dataflow_sources: + - 120 + - 121 + children: + - type: '"""' + id: 120 + range: 20:8 - 20:9 + - type: '"""' + id: 121 + range: 20:13 - 20:14 + - type: '"+"' + id: 122 + range: 20:15 - 20:16 + - type: index_expression + id: 123 + range: 20:17 - 20:27 + children: + - type: selector_expression + id: 124 + range: 20:17 - 20:24 + queries: + - 1 + children: + - type: identifier + id: 125 + range: 20:17 - 20:19 + content: os + - type: '"."' + id: 126 + range: 20:19 - 20:20 + - type: field_identifier + id: 127 + range: 20:20 - 20:24 + content: Args + - type: '"["' + id: 128 + range: 20:24 - 20:25 + - type: int_literal + id: 129 + range: 20:25 - 20:26 + content: "0" + - type: '"]"' + id: 130 + range: 20:26 - 20:27 + - type: '"+"' + id: 131 + range: 20:28 - 20:29 + - type: interpreted_string_literal + id: 132 + range: 20:30 - 20:36 + dataflow_sources: + - 133 + - 134 + children: + - type: '"""' + id: 133 + range: 20:30 - 20:31 + - type: '"""' + id: 134 + range: 20:35 - 20:36 + - type: |- + " + " + id: 135 + range: 20:36 - 21:1 + - type: call_expression + id: 136 + range: 21:2 - 21:17 + dataflow_sources: + - 141 + children: + - type: selector_expression + id: 137 + range: 21:2 - 21:13 + queries: + - 1 + children: + - type: identifier + id: 138 + range: 21:2 - 21:5 + content: fmt + - type: '"."' + id: 139 + range: 21:5 - 21:6 + - type: field_identifier + id: 140 + range: 21:6 - 21:13 + content: Println + - type: argument_list + id: 141 + range: 21:13 - 21:17 + dataflow_sources: + - 142 + - 143 + - 144 + children: + - type: '"("' + id: 142 + range: 21:13 - 21:14 + - type: identifier + id: 143 + range: 21:14 - 21:16 + content: s3 + alias_of: + - 112 + - type: '")"' + id: 144 + range: 21:16 - 21:17 + - type: |- + " + " + id: 145 + range: 21:17 - 22:1 + - type: '"}"' + id: 146 + range: 22:1 - 22:2 + - type: |- + " + " + id: 147 + range: 22:2 - 23:1 + +- node: 10 + content: '"fmt"' + data: + value: fmt + isliteral: true +- node: 15 + content: '"os"' + data: + value: os + isliteral: true +- node: 27 + content: '"Hello World"' + data: + value: Hello World + isliteral: true +- node: 44 + content: Greeting + "!" + data: + value: '*!' + isliteral: false +- node: 56 + content: '"!!"' + data: + value: '!!' + isliteral: true +- node: 75 + content: '"hey "' + data: + value: 'hey ' + isliteral: true +- node: 98 + content: '" there"' + data: + value: ' there' + isliteral: true +- node: 117 + content: '"foo " + os.Args[0] + " bar"' + data: + value: foo * bar + isliteral: false +- node: 47 + content: '"!"' + data: + value: '!' + isliteral: true +- node: 118 + content: '"foo " + os.Args[0]' + data: + value: foo * + isliteral: false +- node: 132 + content: '" bar"' + data: + value: ' bar' + isliteral: true +- node: 119 + content: '"foo "' + data: + value: 'foo ' + isliteral: true + diff --git a/internal/languages/golang/detectors/object/object.go b/internal/languages/golang/detectors/object/object.go index 2d53d3c0f..2730c7fc2 100644 --- a/internal/languages/golang/detectors/object/object.go +++ b/internal/languages/golang/detectors/object/object.go @@ -6,7 +6,6 @@ import ( "github.com/bearer/bearer/internal/scanner/ast/tree" "github.com/bearer/bearer/internal/scanner/detectors/common" - detectorscommon "github.com/bearer/bearer/internal/scanner/detectors/common" "github.com/bearer/bearer/internal/scanner/detectors/types" "github.com/bearer/bearer/internal/scanner/ruleset" ) @@ -14,64 +13,55 @@ import ( type objectDetector struct { types.DetectorBase // Base - classQuery *query.Query - arrayCreationQuery *query.Query + objectQuery *query.Query // Naming assignmentQuery *query.Query // Projection - fieldAccessQuery *query.Query - subscriptExpressionQuery *query.Query + fieldAccessQuery *query.Query } func New(querySet *query.Set) types.Detector { - // $user = new ; + // user = User{} + // user := User{} assignmentQuery := querySet.Add(`[ - (assignment_expression left: (variable_name) @name right: (_) @value) @root + (short_var_declaration left: (expression_list . (identifier) @name) right: (_) @value) @root + (assignment_statement left: (expression_list . (identifier) @name) right: (_) @value) @root ]`) - // class User { - // public $name; - // public $gender; - // function set_name($name) { - // $this->name = $name; - // } - // } - classQuery := querySet.Add(` - ( - class_declaration - name: (name) @class_name - body: ( - declaration_list [ - (property_declaration (property_element (variable_name) @name )) - (method_declaration name: (name) @name) - ] - ) - ) @root`) - - // $user->name; - // $user->name(); + // user.name + // user.name() fieldAccessQuery := querySet.Add(`[ - (member_access_expression object: (_) @object name: (name) @field) @root - (member_call_expression object: (_) @object name: (name) @field) @root + (selector_expression operand: (_) @object field: (_) @field) @root ]`) - // array('foo' => 'bar'); - // [ 'foo' => 'bar' ]; - arrayCreationQuery := querySet.Add(` - (array_creation_expression (array_element_initializer . (_) @key . (_) @value )) @root - `) - - // $user["uuid"]; - subscriptExpressionQuery := querySet.Add(` - (subscript_expression (_) @object (_) @key) @root - `) + // User{ Name: "", Foo: ""} + // type User struct { + // Name string + // } + objectQuery := querySet.Add(`[ + (composite_literal + type: + (type_identifier) @object_name + body: + (literal_value + (keyed_element . (_) @name) + )) @root + ]`) + // func (x User) FullName() string {} -> + // (method_declaration + // receiver: (parameter_list (parameter_declaration type: (type_identifier) @object_name)) + // name: (field_identifier) @name + // ) @root + // (type_declaration + // (type_spec + // name: (type_identifier) @object_name + // type: (struct_type (field_declaration_list (field_declaration name: (field_identifier) @name ))) + // )) @root return &objectDetector{ - classQuery: classQuery, - arrayCreationQuery: arrayCreationQuery, - assignmentQuery: assignmentQuery, - fieldAccessQuery: fieldAccessQuery, - subscriptExpressionQuery: subscriptExpressionQuery, + objectQuery: objectQuery, + assignmentQuery: assignmentQuery, + fieldAccessQuery: fieldAccessQuery, } } @@ -83,17 +73,25 @@ func (detector *objectDetector) DetectAt( node *tree.Node, detectorContext types.Context, ) ([]interface{}, error) { - detections, err := detector.getAssignment(node, detectorContext) - if len(detections) != 0 || err != nil { - return detections, err + if node.Type() == "call_expression" { + detections, err := detectorContext.Scan(node.ChildByFieldName("function"), ruleset.BuiltinObjectRule, traversalstrategy.Cursor) + if err != nil { + return nil, err + } + results := make([]any, len(detections)) + for i, detection := range detections { + results[i] = detection.Data + } + + return results, nil } - detections, err = detector.getClass(node) + detections, err := detector.getAssignment(node, detectorContext) if len(detections) != 0 || err != nil { return detections, err } - detections, err = detector.getArrayCreation(node, detectorContext) + detections, err = detector.getObject(node) if len(detections) != 0 || err != nil { return detections, err } @@ -101,44 +99,36 @@ func (detector *objectDetector) DetectAt( return detector.getProjections(node, detectorContext) } -func (detector *objectDetector) getArrayCreation( - node *tree.Node, - detectorContext types.Context, -) ([]interface{}, error) { - results := detector.arrayCreationQuery.MatchAt(node) +func (detector *objectDetector) getObject(node *tree.Node) ([]interface{}, error) { + results := detector.objectQuery.MatchAt(node) if len(results) == 0 { return nil, nil } - var properties []detectorscommon.Property - for _, result := range results { - pairNode := result["key"] - name := result["value"].Content() + className := results[0]["object_name"].Content() - propertyObjects, err := detectorContext.Scan(result["value"], ruleset.BuiltinObjectRule, traversalstrategy.Cursor) - if err != nil { - return nil, err - } - - if len(propertyObjects) == 0 { - properties = append(properties, detectorscommon.Property{ - Name: name, - Node: pairNode, - }) - - continue - } + var properties []common.Property + for _, result := range results { + nameNode := result["name"] - for _, propertyObject := range propertyObjects { - properties = append(properties, detectorscommon.Property{ - Name: name, - Node: pairNode, - Object: propertyObject, - }) - } + properties = append(properties, common.Property{ + Name: nameNode.Content(), + Node: nameNode, + }) } - return []interface{}{detectorscommon.Object{Properties: properties}}, nil + return []interface{}{common.Object{ + Properties: []common.Property{{ + Name: className, + Object: &types.Detection{ + RuleID: ruleset.BuiltinObjectRule.ID(), + MatchNode: node, + Data: common.Object{ + Properties: properties, + }, + }, + }}, + }}, nil } func (detector *objectDetector) getAssignment( @@ -146,7 +136,6 @@ func (detector *objectDetector) getAssignment( detectorContext types.Context, ) ([]interface{}, error) { result, err := detector.assignmentQuery.MatchOnceAt(node) - if result == nil || err != nil { return nil, err } @@ -173,35 +162,3 @@ func (detector *objectDetector) getAssignment( return objects, nil } - -func (detector *objectDetector) getClass(node *tree.Node) ([]interface{}, error) { - results := detector.classQuery.MatchAt(node) - if len(results) == 0 { - return nil, nil - } - - className := results[0]["class_name"].Content() - - var properties []common.Property - for _, result := range results { - nameNode := result["name"] - - properties = append(properties, common.Property{ - Name: nameNode.Content(), - Node: nameNode, - }) - } - - return []interface{}{common.Object{ - Properties: []common.Property{{ - Name: className, - Object: &types.Detection{ - RuleID: ruleset.BuiltinObjectRule.ID(), - MatchNode: node, - Data: common.Object{ - Properties: properties, - }, - }, - }}, - }}, nil -} diff --git a/internal/languages/golang/detectors/object/projection.go b/internal/languages/golang/detectors/object/projection.go index fc4f7a008..c2df6d7ad 100644 --- a/internal/languages/golang/detectors/object/projection.go +++ b/internal/languages/golang/detectors/object/projection.go @@ -2,10 +2,8 @@ package object import ( "github.com/bearer/bearer/internal/scanner/ast/tree" - "github.com/bearer/bearer/internal/util/stringutil" "github.com/bearer/bearer/internal/scanner/detectors/common" - detectorscommon "github.com/bearer/bearer/internal/scanner/detectors/common" "github.com/bearer/bearer/internal/scanner/detectors/types" ) @@ -35,51 +33,59 @@ func (detector *objectDetector) getProjections( return objects, nil } - result, err = detector.subscriptExpressionQuery.MatchOnceAt(node) - if err != nil { - return nil, err - } + // if result != nil { + // objectNode := result["object_name"] + // objects, err := common.ProjectObject( + // node, + // detectorContext, + // objectNode, + // getObjectName(objectNode), + // result["name"].Content(), + // true, + // ) + // if err != nil { + // return nil, err + // } - if result != nil { - objectNode := result["object"] - propertyName := getElementProperty(result["key"]) - if propertyName == "" { - return nil, nil - } + // return objects, nil + // } - objects, err := detectorscommon.ProjectObject( - node, - detectorContext, - objectNode, - getObjectName(objectNode), - propertyName, - false, - ) - if err != nil { - return nil, err - } + // result, err = detector.subscriptExpressionQuery.MatchOnceAt(node) + // if err != nil { + // return nil, err + // } - return objects, nil - } + // if result != nil { + // objectNode := result["object"] + // propertyName := getElementProperty(result["key"]) + // if propertyName == "" { + // return nil, nil + // } + + // objects, err := detectorscommon.ProjectObject( + // node, + // detectorContext, + // objectNode, + // getObjectName(objectNode), + // propertyName, + // false, + // ) + // if err != nil { + // return nil, err + // } + + // return objects, nil + // } return nil, nil } func getObjectName(objectNode *tree.Node) string { - // $user->name() - // $user->name - if objectNode.Type() == "variable_name" { + // user.name() + // user.name + if objectNode.Type() == "identifier" { return objectNode.Content() } return "" } - -func getElementProperty(node *tree.Node) string { - switch node.Type() { - case "encapsed_string": - return stringutil.StripQuotes(node.Content()) - default: - return node.Content() - } -} diff --git a/internal/languages/golang/detectors/string/string.go b/internal/languages/golang/detectors/string/string.go index 1993b87dd..01c85bc3d 100644 --- a/internal/languages/golang/detectors/string/string.go +++ b/internal/languages/golang/detectors/string/string.go @@ -27,26 +27,21 @@ func (detector *stringDetector) DetectAt( detectorContext types.Context, ) ([]interface{}, error) { switch node.Type() { - case "string": - value := node.Content() - if node.Parent() != nil && node.Parent().Type() != "encapsed_string" { - value = stringutil.StripQuotes(value) + case "binary_expression": + if node.Children()[1].Content() == "+" { + return common.ConcatenateChildStrings(node, detectorContext) } + case "assignment_statement": + if node.Children()[1].Content() == "+=" { + return common.ConcatenateAssignEquals(node, detectorContext) + } + case "interpreted_string_literal", "raw_string_literal": + value := stringutil.StripQuotes(node.Content()) return []interface{}{common.String{ Value: value, IsLiteral: true, }}, nil - case "encapsed_string": - return common.ConcatenateChildStrings(node, detectorContext) - case "binary_expression": - if node.Children()[1].Content() == "." { - return common.ConcatenateChildStrings(node, detectorContext) - } - case "augmented_assignment_expression": - if node.Children()[1].Content() == ".=" { - return common.ConcatenateAssignEquals(node, detectorContext) - } } return nil, nil diff --git a/internal/languages/golang/detectors/testdata/class.go b/internal/languages/golang/detectors/testdata/class.go index 57f2add9a..05117559b 100644 --- a/internal/languages/golang/detectors/testdata/class.go +++ b/internal/languages/golang/detectors/testdata/class.go @@ -1,8 +1,14 @@ -class User -{ - public $name = ''; +package foo - public function LowercaseName() { - echo strtolower($this->name); - } -} \ No newline at end of file +import "fmt" + +type User struct { + firstName string + lastName string + email string + uuid string +} + +func (x User) Name() { + fmt.Println(x.firstName) +} diff --git a/internal/languages/golang/detectors/testdata/no_class.go b/internal/languages/golang/detectors/testdata/no_class.go index 224354883..065475033 100644 --- a/internal/languages/golang/detectors/testdata/no_class.go +++ b/internal/languages/golang/detectors/testdata/no_class.go @@ -1 +1,11 @@ -$user.name(); \ No newline at end of file +package foo + +import "fmt" + +func main() { + user := User{ + email: "foo@bar.com", + } + + fmt.Println(user.email) +} diff --git a/internal/languages/golang/detectors/testdata/string.go b/internal/languages/golang/detectors/testdata/string.go index 4f3713049..1c28b6c8e 100644 --- a/internal/languages/golang/detectors/testdata/string.go +++ b/internal/languages/golang/detectors/testdata/string.go @@ -1,15 +1,22 @@ -class Greet { - const Greeting = "Hello World"; +package main - public static function main($args) - { - $s = self::Greeting . "!"; - $s .= "!!"; +import ( + "fmt" + "os" +) - $s2 = "hey "; - $s2 .= $args[0]; - $s2 .= " there"; +var Greeting = "Hello World" - $s3 = "foo '{$s2}' bar"; - } +func main() { + s := Greeting + "!" + s += "!!" + fmt.Println(s) + + s2 := "hey " + s2 += os.Args[0] + s2 += " there" + fmt.Println(s2) + + s3 := "foo " + os.Args[0] + " bar" + fmt.Println(s3) } diff --git a/internal/languages/golang/golang.go b/internal/languages/golang/golang.go index fbce49db9..ba2412322 100644 --- a/internal/languages/golang/golang.go +++ b/internal/languages/golang/golang.go @@ -4,6 +4,8 @@ import ( sitter "github.com/smacker/go-tree-sitter" "github.com/smacker/go-tree-sitter/golang" + // golang "github.com/bearer/bearer/internal/parser/sitter/golang2" + "github.com/bearer/bearer/internal/classification/schema" "github.com/bearer/bearer/internal/report/detectors" "github.com/bearer/bearer/internal/scanner/ast/query" diff --git a/internal/languages/golang/golang_test.go b/internal/languages/golang/golang_test.go index fd096ae5c..9be9c2a84 100644 --- a/internal/languages/golang/golang_test.go +++ b/internal/languages/golang/golang_test.go @@ -14,9 +14,9 @@ var loggerRule []byte var scopeRule []byte func TestFlow(t *testing.T) { - testhelper.GetRunner(t, loggerRule, "").RunTest(t, "./testdata/testcases/flow", ".snapshots/flow/") + testhelper.GetRunner(t, loggerRule, "Go").RunTest(t, "./testdata/testcases/flow", ".snapshots/flow/") } func TestScope(t *testing.T) { - testhelper.GetRunner(t, scopeRule, "").RunTest(t, "./testdata/scope", ".snapshots/") + testhelper.GetRunner(t, scopeRule, "Go").RunTest(t, "./testdata/scope", ".snapshots/") } diff --git a/internal/languages/golang/pattern/pattern.go b/internal/languages/golang/pattern/pattern.go index e5b8ef731..416f7e0d6 100644 --- a/internal/languages/golang/pattern/pattern.go +++ b/internal/languages/golang/pattern/pattern.go @@ -17,7 +17,7 @@ var ( matchNodeRegex = regexp.MustCompile(`\$`) ellipsisRegex = regexp.MustCompile(`\$<\.\.\.>`) unanchoredPatternNodeTypes = []string{} - patternMatchNodeContainerTypes = []string{"formal_parameters", "simple_parameter", "argument"} + patternMatchNodeContainerTypes = []string{"parameter_declaration", "parameter_list", "var_spec"} allowedPatternQueryTypes = []string{"_"} ) @@ -26,24 +26,8 @@ type Pattern struct { language.PatternBase } -// func (*Pattern) AdjustInput(input string) string { -// return input -// } - -// func (*Pattern) FixupMissing(node *tree.Node) string { -// if node.Type() != `";"` { -// return "" -// } - -// return ";" -// } - -func (*Pattern) FixupVariableDummyValue(input []byte, node *tree.Node, dummyValue string) string { - if slices.Contains([]string{"named_type"}, node.Parent().Type()) { - return "$" + dummyValue - } - - return dummyValue +func (*Pattern) AdjustInput(input string) string { + return input + "\n" } func (*Pattern) ExtractVariables(input string) (string, []language.PatternVariable, error) { @@ -100,23 +84,24 @@ func (*Pattern) FindUnanchoredPoints(input []byte) [][]int { func (*Pattern) IsLeaf(node *tree.Node) bool { // Encapsed string literal switch node.Type() { - case "encapsed_string": - namedChildren := node.NamedChildren() - if len(namedChildren) == 1 && namedChildren[0].Type() == "string" { - return true - } + case "raw_string_literal", "interpreted_string_literal": + return true } return false } func (*Pattern) LeafContentTypes() []string { return []string{ - "encapsed_string", - "string", - "name", - "integer", - "float", - "boolean", + "identifier", + "package_identifier", + "type_identifier", + "raw_string_literal", + "intepreted_string_literal", + "int_literal", + "float_literal", + "true", + "false", + "nil", } } @@ -130,13 +115,8 @@ func (*Pattern) IsAnchored(node *tree.Node) (bool, bool) { return true, true } - if parent.Type() == "method_declaration" { - // visibility - if node == parent.ChildByFieldName("name") { - return false, true - } - - // type + if parent.Type() == "function_declaration" { + // parameters if node == parent.ChildByFieldName("parameters") { return true, false } @@ -144,19 +124,10 @@ func (*Pattern) IsAnchored(node *tree.Node) (bool, bool) { return false, false } - // Associative array elements are unanchored - // eg. array("foo" => 42) - if parent.Type() == "array_creation_expression" && - node.Type() == "array_element_initializer" && - len(node.NamedChildren()) == 2 { - return false, false - } - - // Class body declaration_list - // function/block compound_statement + // function declaration_list unAnchored := []string{ - "declaration_list", - "compound_statement", + "function_declaration", + "var_declaration", } isUnanchored := !slices.Contains(unAnchored, parent.Type()) @@ -164,31 +135,14 @@ func (*Pattern) IsAnchored(node *tree.Node) (bool, bool) { } func (*Pattern) IsRoot(node *tree.Node) bool { - return !slices.Contains([]string{"expression_statement", "program"}, node.Type()) && !node.IsMissing() + return !slices.Contains([]string{"source_file"}, node.Type()) && !node.IsMissing() } func (patternLanguage *Pattern) NodeTypes(node *tree.Node) []string { - parent := node.Parent() - if parent == nil { - return []string{node.Type()} - } - - if (node.Type() == "string" && parent.Type() != "encapsed_string") || - (node.Type() == "encapsed_string" && patternLanguage.IsLeaf(node)) { - return []string{"encapsed_string", "string"} - } - return []string{node.Type()} } func (*Pattern) TranslateContent(fromNodeType, toNodeType, content string) string { - if fromNodeType == "string" && toNodeType == "encapsed_string" { - return fmt.Sprintf(`"%s"`, content[1:len(content)-1]) - } - if fromNodeType == "encapsed_string" && toNodeType == "string" { - return fmt.Sprintf("'%s'", content[1:len(content)-1]) - } - return content } diff --git a/internal/languages/golang/testdata/logger.yml b/internal/languages/golang/testdata/logger.yml index 7be35f485..8cf1f1cbb 100644 --- a/internal/languages/golang/testdata/logger.yml +++ b/internal/languages/golang/testdata/logger.yml @@ -1,10 +1,19 @@ type: "risk" languages: - - + - go patterns: - - pattern: error_log($) + - pattern: log.$().$($) filters: + - variable: LOGLEVEL + values: + - Error + - Debug + - variable: METHOD + values: + - Msgf + - Msg - variable: DATA_TYPE detection: datatype + scope: result metadata: id: rule_logger_test diff --git a/internal/languages/golang/testdata/scope/scope.go b/internal/languages/golang/testdata/scope/scope.go index 73504fdc1..749ac9b44 100644 --- a/internal/languages/golang/testdata/scope/scope.go +++ b/internal/languages/golang/testdata/scope/scope.go @@ -1,17 +1,42 @@ -scopeCursor($_GET["oops"]); -scopeCursor(x . $_GET["ok"]); -scopeCursor(x ? $_GET["oops"] : y); -scopeCursor($_GET["ok"] ? x : y); -scopeCursor($_GET["oops"] ?: y); - -scopeNested($_GET["oops"]); -scopeNested(x . $_GET["oops"]); -scopeNested(x ? $_GET["oops"] : y); -scopeNested($_GET["oops"] ? x : y); -scopeNested($_GET["oops"] ?: y); - -scopeResult($_GET["oops"]); -scopeResult(x . $_GET["oops"]); -scopeResult(x ? $_GET["oops"] : y); -scopeResult($_GET["ok"] ? x : y); -scopeResult($_GET["oops"] ?: y); \ No newline at end of file +package foo + +import ( + "net/http" +) + +func scopeCursor(s any) any { return s } // nolint: unused +func scopeNested(s any) any { return s } // nolint: unused +func scopeResult(s any) any { return s } // nolint: unused + +func loginHandler(w http.ResponseWriter, request *http.Request) { // nolint: unused + var x string + y := map[string]string{} + + scopeCursor(request.FormValue("oops")) // nolint: staticcheck + scopeCursor(x + request.FormValue("ok")) // nolint: staticcheck + + scopeNested(request.FormValue("oops")) // nolint: staticcheck + scopeNested(x + request.FormValue("oops")) // nolint: staticcheck + scopeNested(y[request.FormValue("oops")]) // nolint: staticcheck + + scopeResult(request.FormValue("oops")) // nolint: staticcheck + scopeResult(x + request.FormValue("oops")) // nolint: staticcheck + scopeResult(y[request.FormValue("ok")]) // nolint: staticcheck +} + +func main() { // nolint: unused + var req *http.Request + var x string + y := map[string]string{} + + scopeCursor(req.FormValue("oops")) // nolint: staticcheck + scopeCursor(x + req.FormValue("ok")) // nolint: staticcheck + + scopeNested(req.FormValue("oops")) // nolint: staticcheck + scopeNested(x + req.FormValue("oops")) // nolint: staticcheck + scopeNested(y[req.FormValue("oops")]) // nolint: staticcheck + + scopeResult(req.FormValue("oops")) // nolint: staticcheck + scopeResult(x + req.FormValue("oops")) // nolint: staticcheck + scopeResult(y[req.FormValue("ok")]) // nolint: staticcheck +} diff --git a/internal/languages/golang/testdata/scope_rule.yml b/internal/languages/golang/testdata/scope_rule.yml index 0cf504ea8..f88849691 100644 --- a/internal/languages/golang/testdata/scope_rule.yml +++ b/internal/languages/golang/testdata/scope_rule.yml @@ -1,5 +1,5 @@ languages: - - + - go patterns: - pattern: scopeCursor($) filters: @@ -19,8 +19,16 @@ patterns: auxiliary: - id: scope_test_user_input patterns: - - $_GET[$<_>] - - $_POST[$<_>] + - pattern: $.FormValue($<_>) + filters: + - variable: HTTP_REQUEST + detection: scope_test_http_request_init + - id: scope_test_http_request_init + patterns: + - func $<_>($<...>$$<_> *http.Request$<...>) + - func $<_>($<...>$$<_> http.Request$<...>) + - var $$<_> *http.Request + - var $$<_> http.Request severity: high metadata: description: Test detection filter scopes diff --git a/internal/languages/golang/testdata/testcases/flow/different-line.go b/internal/languages/golang/testdata/testcases/flow/different-line.go index ef092479a..2710848a5 100644 --- a/internal/languages/golang/testdata/testcases/flow/different-line.go +++ b/internal/languages/golang/testdata/testcases/flow/different-line.go @@ -1,3 +1,32 @@ -$user = new User(); -$name = $user->name; -error_log($name); \ No newline at end of file +package main3 + +import "github.com/rs/zerolog/log" + +// var a, b string + +type User struct { + Name string + Uuid string + Gender string +} + +func (x User) FullName() (string, errror) { + return "[" + x.Gender + "] " + x.Name, nil +} + +func main() { + user := User{ + Uuid: "123", + Name: "foo", + Gender: nil, + } + + name := user.Name + uuid := user.Uuid + other, _ := user.FullName() + + log.Error().Msg(uuid) // ok + log.Error().Msg(name) // expect detection + log.Error().Msg(other) // expect detection + log.Error().Msg(user) // expect detection +} diff --git a/internal/languages/golang/testdata/testcases/flow/same-line.go b/internal/languages/golang/testdata/testcases/flow/same-line.go index d10733f42..efbb86b24 100644 --- a/internal/languages/golang/testdata/testcases/flow/same-line.go +++ b/internal/languages/golang/testdata/testcases/flow/same-line.go @@ -1,2 +1,22 @@ -error_log($user->name); -error_log($user->name()); \ No newline at end of file +package main2 + +import "github.com/rs/zerolog/log" + +type User struct { + Name string +} + +func (x User) FullName() string { + return x.Name +} + +func main() { + user := User{ + Name: "foo", + Gender: "female", + } + + log.Error().Msg(user.Name) // expect detection + log.Error().Msg(user.FullName()) // expect detection + log.Error().Msgf("user info %s", user.FullName()) // expect detection +}