Projects STRLCPY bearer Commits 048156e0
🤬
  • fix: don't report sub datatype detections (#751)

    * fix: don't report nested datatype detections
    
    * feat: nested objects/datatypes for javascript
    
    * feat: nested objects/datatypes for ruby
    
    * feat: support nested objects in datatype detector
    
    * feat: support nested datatypes in reporting
    
    * feat: support method calls
    
    * feat: ruby allow certain nested calls
    
    * feat: allow certain nested javascript calls
    
    * fix: don't look for datatype in express jwt not revoked
  • Loading...
  • David Roe committed with GitHub 1 year ago
    048156e0
    1 parent 5cbaf628
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/composition.go
    skipped 18 lines
    19 19   pluralizer := pluralize.NewClient()
    20 20   
    21 21   for _, detection := range detections {
     22 + detectorType := detectors.Type(detection.DetectorType)
    22 23   data := detection.Data.(custom.Data)
    23 24   
    24 25   if len(data.Datatypes) == 0 {
    25 26   report.AddDetection(reportdetections.TypeCustomRisk,
    26  - detectors.Type(detection.DetectorType),
     27 + detectorType,
    27 28   source.New(
    28 29   file,
    29 30   file.Path,
    skipped 8 lines
    38 39   }
    39 40   
    40 41   for _, datatypeDetection := range data.Datatypes {
    41  - datatypeData := datatypeDetection.Data.(datatype.Data)
    42  - 
    43  - report.AddDetection(
    44  - reportdetections.TypeCustomClassified,
    45  - detectors.Type(detection.DetectorType),
    46  - source.New(
    47  - file,
    48  - file.Path,
    49  - datatypeDetection.MatchNode.LineNumber(),
    50  - datatypeDetection.MatchNode.ColumnNumber(),
    51  - "",
    52  - ),
    53  - schema.Schema{
    54  - ObjectName: datatypeData.Name,
    55  - NormalizedObjectName: pluralizer.Singular(strings.ToLower(datatypeData.Name)),
    56  - Classification: datatypeData.Classification,
    57  - Parent: &schema.Parent{
    58  - LineNumber: detection.MatchNode.LineNumber(),
    59  - Content: detection.MatchNode.Content(),
    60  - },
    61  - },
     42 + reportDatatypeDetection(
     43 + report,
     44 + pluralizer,
     45 + file,
     46 + detectorType,
     47 + detection,
     48 + datatypeDetection,
     49 + "",
    62 50   )
     51 + }
     52 + }
     53 +}
    63 54   
    64  - for _, property := range datatypeData.Properties {
     55 +func reportDatatypeDetection(
     56 + report reportdetections.ReportDetection,
     57 + pluralizer *pluralize.Client,
     58 + file *file.FileInfo,
     59 + detectorType detectors.Type,
     60 + detection,
     61 + datatypeDetection *detectortypes.Detection,
     62 + objectName string,
     63 +) {
     64 + data := datatypeDetection.Data.(datatype.Data)
    65 65   
    66  - report.AddDetection(
    67  - reportdetections.TypeCustomClassified,
    68  - detectors.Type(detection.DetectorType),
    69  - source.New(
    70  - file,
    71  - file.Path,
    72  - property.Detection.MatchNode.LineNumber(),
    73  - property.Detection.MatchNode.ColumnNumber(),
    74  - "",
    75  - ),
    76  - schema.Schema{
    77  - ObjectName: datatypeData.Name,
    78  - NormalizedObjectName: pluralizer.Singular(strings.ToLower(property.Name)),
    79  - FieldName: property.Name,
    80  - NormalizedFieldName: pluralizer.Singular(strings.ToLower(property.Name)),
    81  - Classification: property.Classification,
    82  - Parent: &schema.Parent{
    83  - LineNumber: detection.MatchNode.LineNumber(),
    84  - Content: detection.MatchNode.Content(),
    85  - },
    86  - },
    87  - )
    88  - }
     66 + for _, property := range data.Properties {
     67 + report.AddDetection(
     68 + reportdetections.TypeCustomClassified,
     69 + detectorType,
     70 + source.New(
     71 + file,
     72 + file.Path,
     73 + property.Node.LineNumber(),
     74 + property.Node.ColumnNumber(),
     75 + "",
     76 + ),
     77 + schema.Schema{
     78 + ObjectName: objectName,
     79 + NormalizedObjectName: pluralizer.Singular(strings.ToLower(objectName)),
     80 + FieldName: property.Name,
     81 + NormalizedFieldName: pluralizer.Singular(strings.ToLower(property.Name)),
     82 + Classification: property.Classification,
     83 + Parent: &schema.Parent{
     84 + LineNumber: detection.MatchNode.LineNumber(),
     85 + Content: detection.MatchNode.Content(),
     86 + },
     87 + },
     88 + )
     89 + 
     90 + if property.Datatype != nil {
     91 + reportDatatypeDetection(
     92 + report,
     93 + pluralizer,
     94 + file,
     95 + detectorType,
     96 + detection,
     97 + property.Datatype,
     98 + property.Name,
     99 + )
    89 100   }
    90 101   }
    91  - 
    92 102  }
    93 103   
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/javascript.go
    skipped 16 lines
    17 17   "github.com/bearer/bearer/new/detector/implementation/generic/insecureurl"
    18 18   "github.com/bearer/bearer/new/detector/implementation/generic/stringliteral"
    19 19   "github.com/bearer/bearer/new/detector/implementation/javascript/object"
    20  - "github.com/bearer/bearer/new/detector/implementation/javascript/property"
    21 20   "github.com/bearer/bearer/new/language"
    22 21   
    23 22   stringdetector "github.com/bearer/bearer/new/detector/implementation/javascript/string"
    24 23   detectorset "github.com/bearer/bearer/new/detector/set"
    25 24   detectortypes "github.com/bearer/bearer/new/detector/types"
     25 + "github.com/bearer/bearer/new/language/implementation"
     26 + "github.com/bearer/bearer/new/language/implementation/javascript"
    26 27   languagetypes "github.com/bearer/bearer/new/language/types"
    27 28  )
    28 29   
    29 30  type Composition struct {
    30 31   customDetectorTypes []string
    31 32   detectorSet detectortypes.DetectorSet
     33 + langImplementation implementation.Implementation
    32 34   lang languagetypes.Language
    33 35   closers []func()
    34 36  }
    skipped 5 lines
    40 42   }
    41 43   
    42 44   composition := &Composition{
    43  - lang: lang,
     45 + langImplementation: javascript.Get(),
     46 + lang: lang,
    44 47   }
    45 48   
    46 49   staticDetectors := []struct {
    skipped 3 lines
    50 53   {
    51 54   constructor: object.New,
    52 55   name: "object detector",
    53  - },
    54  - {
    55  - constructor: property.New,
    56  - name: "property detector",
    57 56   },
    58 57   {
    59 58   constructor: stringdetector.New,
    skipped 119 lines
    179 178   return nil, fmt.Errorf("failed to parse file %s", err)
    180 179   }
    181 180   
    182  - evaluator := evaluator.New(composition.lang, composition.detectorSet, tree, file.FileInfo.Name())
     181 + evaluator := evaluator.New(
     182 + composition.langImplementation,
     183 + composition.lang,
     184 + composition.detectorSet,
     185 + tree,
     186 + file.FileInfo.Name(),
     187 + )
    183 188   
    184 189   var result []*detectortypes.Detection
    185 190   for _, detectorType := range detectorTypes {
    skipped 11 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/javascript_test.go
    skipped 16 lines
    17 17  //go:embed testdata/deconstructing.yml
    18 18  var deconstructingRule []byte
    19 19   
    20  -//go:embed testdata/class.yml
    21  -var classNameRule []byte
    22  - 
    23  -func TestClass(t *testing.T) {
    24  - testhelper.RunTest(t, map[string][]byte{
    25  - "class_name": classNameRule,
    26  - }, "testdata/testcases/class", javascript.New)
    27  -}
    28  - 
    29 20  func TestFlow(t *testing.T) {
    30 21   testhelper.RunTest(t, map[string][]byte{
    31 22   "logger": datatypeRule,
    32 23   }, "testdata/testcases/flow", javascript.New)
    33  -}
    34  - 
    35  -func TestObject(t *testing.T) {
    36  - testhelper.RunTest(t, map[string][]byte{
    37  - "logger": datatypeRule,
    38  - }, "testdata/testcases/object", javascript.New)
    39  -}
    40  - 
    41  -func TestObjectMemberExpression(t *testing.T) {
    42  - testhelper.RunTest(t, map[string][]byte{
    43  - "logger": datatypeRule,
    44  - }, "testdata/testcases/object-member-expression", javascript.New)
    45  -}
    46  - 
    47  -func TestObjectSubscriptExpression(t *testing.T) {
    48  - testhelper.RunTest(t, map[string][]byte{
    49  - "logger": datatypeRule,
    50  - }, "testdata/testcases/object-subscript-expression", javascript.New)
    51 24  }
    52 25   
    53 26  func TestObjectDeconstructing(t *testing.T) {
    skipped 11 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/class.yml
    1  -type: "risk"
    2  -languages:
    3  - - javascript
    4  -patterns:
    5  - - pattern: |
    6  - $<DATA_TYPE>
    7  - filters:
    8  - - variable: DATA_TYPE
    9  - detection: datatype
    10  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/class/constructor.js
    1  -class User {
    2  - constructor(name, city) {}
    3  -}
    4  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/class/method.js
    1  -class User {
    2  - getName()
    3  -}
    4  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/class/name.js
    1  -class User {}
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object/nested.js
    1  -console.log({ user: { city: "New York" } });
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object/quoted.js
    1  -console.log({ "user": { "name": "mike" } });
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object/simple.js
    1  -console.log({ user: "mike" });
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-member-expression/intertwined.js
    1  -console.log(user["address"]["city"].zip);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-member-expression/nested-arguments.js
    1  -console.log(user.address().city.zip);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-member-expression/nested.js
    1  -console.log(user.address.city);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-member-expression/simple.js
    1  -console.log(user.name);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-subscript-expression/intertwined.js
    1  -console.log(user["address"]["city"].zip);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-subscript-expression/nested-arguments.js
    1  -console.log(user["address"]()["city"]["zip"]);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-subscript-expression/nested.js
    1  -console.log(user["address"]["city"]);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/javascript/testdata/testcases/object-subscript-expression/simple.js
    1  -console.log(user["name"]);
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/composition/ruby/ruby.go
    skipped 12 lines
    13 13   "github.com/bearer/bearer/new/detector/implementation/generic/insecureurl"
    14 14   "github.com/bearer/bearer/new/detector/implementation/generic/stringliteral"
    15 15   "github.com/bearer/bearer/new/detector/implementation/ruby/object"
    16  - "github.com/bearer/bearer/new/detector/implementation/ruby/property"
    17 16   "github.com/bearer/bearer/new/language"
    18 17   
    19 18   "github.com/bearer/bearer/pkg/classification"
    skipped 3 lines
    23 22   stringdetector "github.com/bearer/bearer/new/detector/implementation/ruby/string"
    24 23   detectorset "github.com/bearer/bearer/new/detector/set"
    25 24   detectortypes "github.com/bearer/bearer/new/detector/types"
     25 + "github.com/bearer/bearer/new/language/implementation"
     26 + "github.com/bearer/bearer/new/language/implementation/ruby"
    26 27   languagetypes "github.com/bearer/bearer/new/language/types"
    27 28  )
    28 29   
    29 30  type Composition struct {
    30 31   customDetectorTypes []string
    31 32   detectorSet detectortypes.DetectorSet
     33 + langImplementation implementation.Implementation
    32 34   lang languagetypes.Language
    33 35   closers []func()
    34 36  }
    skipped 5 lines
    40 42   }
    41 43   
    42 44   composition := &Composition{
    43  - lang: lang,
     45 + langImplementation: ruby.Get(),
     46 + lang: lang,
    44 47   }
    45 48   
    46 49   staticDetectors := []struct {
    skipped 3 lines
    50 53   {
    51 54   constructor: object.New,
    52 55   name: "object detector",
    53  - },
    54  - {
    55  - constructor: property.New,
    56  - name: "property detector",
    57 56   },
    58 57   {
    59 58   constructor: stringdetector.New,
    skipped 118 lines
    178 177   return nil, fmt.Errorf("failed to parse file %s", err)
    179 178   }
    180 179   
    181  - evaluator := evaluator.New(composition.lang, composition.detectorSet, tree, file.FileInfo.Name())
     180 + evaluator := evaluator.New(
     181 + composition.langImplementation,
     182 + composition.lang,
     183 + composition.detectorSet,
     184 + tree,
     185 + file.FileInfo.Name(),
     186 + )
    182 187   
    183 188   var result []*detectortypes.Detection
    184 189   for _, detectorType := range detectorTypes {
    skipped 11 lines
  • ■ ■ ■ ■ ■
    new/detector/evaluator/evaluator.go
    skipped 4 lines
    5 5   "strings"
    6 6   
    7 7   "github.com/bearer/bearer/new/detector/types"
     8 + "github.com/bearer/bearer/new/language/implementation"
    8 9   langtree "github.com/bearer/bearer/new/language/tree"
    9 10   languagetypes "github.com/bearer/bearer/new/language/types"
    10 11   "golang.org/x/exp/slices"
    11 12  )
    12 13   
    13 14  type evaluator struct {
     15 + langImplementation implementation.Implementation
    14 16   lang languagetypes.Language
    15 17   detectorSet types.DetectorSet
    16 18   detectionCache map[langtree.NodeID]map[string][]*types.Detection
    skipped 3 lines
    20 22  }
    21 23   
    22 24  func New(
     25 + langImplementation implementation.Implementation,
    23 26   lang languagetypes.Language,
    24 27   detectorSet types.DetectorSet,
    25 28   tree *langtree.Tree,
    skipped 2 lines
    28 31   detectionCache := make(map[langtree.NodeID]map[string][]*types.Detection)
    29 32   
    30 33   return &evaluator{
     34 + langImplementation: langImplementation,
    31 35   lang: lang,
    32 36   fileName: fileName,
    33 37   detectorSet: detectorSet,
    skipped 13 lines
    47 51   followFlow bool,
    48 52  ) ([]*types.Detection, error) {
    49 53   var result []*types.Detection
     54 + var nestedMode bool
    50 55   
    51 56   if err := rootNode.Walk(func(node *langtree.Node, visitChildren func() error) error {
     57 + if nestedMode && !evaluator.langImplementation.PassthroughNested(node) {
     58 + return nil
     59 + }
     60 + 
    52 61   detections, err := evaluator.nonUnifiedNodeDetections(node, detectorType)
    53 62   if err != nil {
    54 63   return err
    55 64   }
    56  - 
    57  - result = append(result, detections...)
    58 65   
    59 66   if followFlow {
    60 67   for _, unifiedNode := range node.UnifiedNodes() {
    skipped 2 lines
    63 70   return err
    64 71   }
    65 72   
    66  - result = append(result, unifiedNodeDetections...)
     73 + detections = append(detections, unifiedNodeDetections...)
    67 74   }
    68 75   }
     76 + 
     77 + previousNestedMode := nestedMode
    69 78   
    70 79   if len(detections) != 0 {
    71 80   nestedDetections, err := evaluator.detectorSet.NestedDetections(detectorType)
    skipped 2 lines
    74 83   }
    75 84   
    76 85   if !nestedDetections {
    77  - return nil
     86 + nestedMode = true
    78 87   }
    79 88   }
    80 89   
    81  - return visitChildren()
     90 + result = append(result, detections...)
     91 + 
     92 + err = visitChildren()
     93 + nestedMode = previousNestedMode
     94 + return err
    82 95   }); err != nil {
    83 96   return nil, err
    84 97   }
    skipped 216 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/custom/filter.go
    skipped 104 lines
    105 105   contains bool,
    106 106  ) (*bool, []*types.Detection, error) {
    107 107   var evaluateDetections func(*tree.Node, string, bool) ([]*types.Detection, error)
    108  - var evaluateHasDetection func(*tree.Node, string) (bool, error)
    109 108   if contains {
    110 109   evaluateDetections = evaluator.ForTree
    111  - evaluateHasDetection = evaluator.TreeHas
    112 110   } else {
    113 111   evaluateDetections = evaluator.ForNode
    114  - evaluateHasDetection = evaluator.NodeHas
    115 112   }
    116 113   
    117 114   if detectorType == "datatype" {
    skipped 2 lines
    120 117   return boolPointer(len(detections) != 0), detections, err
    121 118   }
    122 119   
    123  - hasDetection, err := evaluateHasDetection(node, detectorType)
    124  - return boolPointer(hasDetection), nil, err
     120 + detections, err := evaluateDetections(node, detectorType, true)
     121 + var datatypeDetections []*types.Detection
     122 + 
     123 + for _, detection := range detections {
     124 + if data, ok := detection.Data.(Data); ok {
     125 + datatypeDetections = append(datatypeDetections, data.Datatypes...)
     126 + }
     127 + }
     128 + 
     129 + return boolPointer(len(detections) != 0), datatypeDetections, err
    125 130  }
    126 131   
    127 132  func matchContentFilter(filter settings.PatternFilter, content string) *bool {
    skipped 59 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/.snapshots/TestDatatypeDetector-datatype
     1 +- position: "1:1"
     2 + content: |-
     3 + x = {
     4 + y: {
     5 + user: { first_name: "" }
     6 + },
     7 + email: ""
     8 + }
     9 + data:
     10 + properties:
     11 + - name: x
     12 + classification:
     13 + name: x
     14 + datatype: null
     15 + decision:
     16 + state: valid
     17 + reason: invalid_object_with_valid_properties
     18 + datatype:
     19 + detectortype: datatype
     20 + matchnode: {}
     21 + data:
     22 + properties:
     23 + - name: "y"
     24 + classification:
     25 + name: "y"
     26 + datatype: null
     27 + decision:
     28 + state: invalid
     29 + reason: invalid_property
     30 + datatype:
     31 + detectortype: datatype
     32 + matchnode: {}
     33 + data:
     34 + properties:
     35 + - name: user
     36 + classification:
     37 + name: user
     38 + datatype: null
     39 + decision:
     40 + state: valid
     41 + reason: valid_object_with_valid_properties
     42 + datatype:
     43 + detectortype: datatype
     44 + matchnode: {}
     45 + data:
     46 + properties:
     47 + - name: first_name
     48 + classification:
     49 + name: first name
     50 + subject_name: User
     51 + datatype:
     52 + name: Firstname
     53 + uuid: 380c8cde-ca2e-44ed-82db-2ab1e7c255c7
     54 + category_uuid: 14124881-6b92-4fc5-8005-ea7c1c09592e
     55 + decision:
     56 + state: valid
     57 + reason: known_pattern
     58 + datatype: null
     59 + - name: email
     60 + classification:
     61 + name: email
     62 + datatype:
     63 + name: Email Address
     64 + uuid: 22e24c62-82d3-4b72-827c-e261533331bd
     65 + category_uuid: cef587dd-76db-430b-9e18-7b031e1a193b
     66 + decision:
     67 + state: valid
     68 + reason: valid_unknown_pattern
     69 + datatype: null
     70 + 
     71 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/datatype/datatype.go
    1 1  package datatype
    2 2   
    3 3  import (
     4 + generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    4 5   "github.com/bearer/bearer/new/detector/types"
    5 6   "github.com/bearer/bearer/new/language/tree"
     7 + languagetypes "github.com/bearer/bearer/new/language/types"
     8 + classificationschema "github.com/bearer/bearer/pkg/classification/schema"
    6 9   "github.com/bearer/bearer/pkg/report/detectors"
    7 10   "github.com/bearer/bearer/pkg/report/schema"
    8  - 
    9  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    10  - languagetypes "github.com/bearer/bearer/new/language/types"
    11  - classificationschema "github.com/bearer/bearer/pkg/classification/schema"
     11 + "github.com/bearer/bearer/pkg/util/classify"
    12 12  )
    13 13   
    14 14  type Data struct {
    15  - Name string
    16  - Classification classificationschema.Classification
    17  - Properties []Property
    18  -}
    19  - 
    20  -func (data *Data) toClassifcationRequestDetection() *classificationschema.ClassificationRequestDetection {
    21  - req := &classificationschema.ClassificationRequestDetection{
    22  - Name: data.Name,
    23  - SimpleType: schema.SimpleTypeUnknown,
    24  - }
    25  - for _, property := range data.Properties {
    26  - req.Properties = append(req.Properties, &classificationschema.ClassificationRequestDetection{
    27  - Name: property.Name,
    28  - SimpleType: schema.SimpleTypeUnknown,
    29  - Properties: []*classificationschema.ClassificationRequestDetection{},
    30  - })
    31  - }
    32  - return req
     15 + Properties []Property
    33 16  }
    34 17   
    35 18  type Property struct {
    36 19   Name string
    37  - Detection *types.Detection
     20 + Node *tree.Node
    38 21   Classification classificationschema.Classification
     22 + Datatype *types.Detection
    39 23  }
    40 24   
    41 25  type datatypeDetector struct {
    skipped 9 lines
    51 35   
    52 36  func (detector *datatypeDetector) Name() string {
    53 37   return "datatype"
     38 +}
     39 + 
     40 +func (detector *datatypeDetector) NestedDetections() bool {
     41 + return false
    54 42  }
    55 43   
    56 44  func (detector *datatypeDetector) DetectAt(
    skipped 8 lines
    65 53   var result []interface{}
    66 54   
    67 55   for _, object := range objectDetections {
    68  - var properties []Property
     56 + data, _ := detector.classifyObject(evaluator.FileName(), "", object)
     57 + result = append(result, data)
     58 + }
     59 + 
     60 + return result, nil
     61 +}
    69 62   
    70  - objectData := object.Data.(generictypes.Object)
     63 +func (detector *datatypeDetector) Close() {}
    71 64   
    72  - for _, property := range objectData.Properties {
    73  - propertyData := property.Data.(generictypes.Property)
    74  - properties = append(properties, Property{
    75  - Detection: property,
    76  - Name: propertyData.Name,
    77  - })
     65 +func (detector *datatypeDetector) classifyObject(
     66 + filename,
     67 + name string,
     68 + detection *types.Detection,
     69 +) (Data, classificationschema.Classification) {
     70 + objectData := detection.Data.(generictypes.Object)
     71 + 
     72 + classification := detector.classifier.Classify(buildClassificationRequest(filename, name, objectData))
     73 + 
     74 + properties := make([]Property, len(objectData.Properties))
     75 + 
     76 + // NOTE: assumption is that classification will have all properties that detection has in same order
     77 + for i, property := range objectData.Properties {
     78 + propertyDetection, propertyClassification := detector.classifyProperty(
     79 + filename,
     80 + property.Name,
     81 + property.Object,
     82 + classification.Properties[i].Classification,
     83 + )
     84 + 
     85 + node := property.Node
     86 + if node == nil {
     87 + node = detection.MatchNode
    78 88   }
    79 89   
    80  - data := Data{
    81  - Name: objectData.Name,
    82  - Properties: properties,
     90 + properties[i] = Property{
     91 + Datatype: propertyDetection,
     92 + Node: node,
     93 + Name: property.Name,
     94 + Classification: propertyClassification,
    83 95   }
     96 + }
    84 97   
    85  - classificationReqDetection := data.toClassifcationRequestDetection()
     98 + return Data{Properties: properties}, classification.Classification
     99 +}
    86 100   
    87  - classification := detector.classifier.Classify(classificationschema.ClassificationRequest{
    88  - Value: classificationReqDetection,
    89  - DetectorType: detectors.DetectorRuby,
    90  - Filename: evaluator.FileName(),
    91  - })
     101 +func (detector *datatypeDetector) classifyProperty(
     102 + filename,
     103 + name string,
     104 + detection *types.Detection,
     105 + parentClassification classificationschema.Classification,
     106 +) (*types.Detection, classificationschema.Classification) {
     107 + if detection == nil {
     108 + return nil, parentClassification
     109 + }
    92 110   
    93  - mergeClassification(&data, classification)
     111 + data, propertyClassification := detector.classifyObject(filename, name, detection)
    94 112   
    95  - result = append(result, data)
     113 + propertyDetection := &types.Detection{
     114 + DetectorType: "datatype",
     115 + MatchNode: detection.MatchNode,
     116 + Data: data,
     117 + }
     118 + 
     119 + if parentClassification.Decision.State == classify.Valid ||
     120 + (parentClassification.Decision.State == classify.Potential && propertyClassification.Decision.State == classify.Invalid) ||
     121 + (parentClassification.Decision.State == classify.Invalid && propertyClassification.Decision.State == classify.Invalid) {
     122 + return propertyDetection, parentClassification
    96 123   }
    97 124   
    98  - return result, nil
     125 + return propertyDetection, propertyClassification
    99 126  }
    100 127   
    101  -func (detector *datatypeDetector) Close() {}
     128 +func buildClassificationRequest(filename, name string, data generictypes.Object) classificationschema.ClassificationRequest {
     129 + var properties []*classificationschema.ClassificationRequestDetection
    102 130   
    103  -// NOTE: presumption for mergeClassification is that classification will have all properties that detection has in same order
    104  -func mergeClassification(detection *Data, classification *classificationschema.ClassifiedDatatype) {
    105  - detection.Classification = classification.Classification
    106  - for i := range detection.Properties {
    107  - detection.Properties[i].Classification = classification.Properties[i].Classification
     131 + for _, property := range data.Properties {
     132 + properties = append(properties, &classificationschema.ClassificationRequestDetection{
     133 + Name: property.Name,
     134 + SimpleType: schema.SimpleTypeUnknown,
     135 + })
     136 + }
     137 + 
     138 + return classificationschema.ClassificationRequest{
     139 + Value: &classificationschema.ClassificationRequestDetection{
     140 + Name: name,
     141 + SimpleType: schema.SimpleTypeUnknown,
     142 + Properties: properties,
     143 + },
     144 + DetectorType: detectors.DetectorRuby,
     145 + Filename: filename,
    108 146   }
    109 147  }
    110 148   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/generic.go
     1 +package generic
     2 + 
     3 +import (
     4 + generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
     5 + "github.com/bearer/bearer/new/detector/types"
     6 + "github.com/bearer/bearer/new/language/tree"
     7 +)
     8 + 
     9 +func GetNonVirtualObjects(evaluator types.Evaluator, node *tree.Node) ([]*types.Detection, error) {
     10 + detections, err := evaluator.ForNode(node, "object", true)
     11 + if err != nil {
     12 + return nil, err
     13 + }
     14 + 
     15 + var result []*types.Detection
     16 + for _, detection := range detections {
     17 + data := detection.Data.(generictypes.Object)
     18 + if !data.IsVirtual {
     19 + result = append(result, detection)
     20 + }
     21 + }
     22 + 
     23 + return result, nil
     24 +}
     25 + 
     26 +func ProjectObject(
     27 + node *tree.Node,
     28 + evaluator types.Evaluator,
     29 + objectNode *tree.Node,
     30 + objectName,
     31 + propertyName string,
     32 + isPropertyAccess bool,
     33 +) ([]interface{}, error) {
     34 + var result []interface{}
     35 + 
     36 + if isPropertyAccess {
     37 + objectDetections, err := GetNonVirtualObjects(evaluator, objectNode)
     38 + if err != nil {
     39 + return nil, err
     40 + }
     41 + 
     42 + for _, objectDetection := range objectDetections {
     43 + objectData := objectDetection.Data.(generictypes.Object)
     44 + 
     45 + for _, property := range objectData.Properties {
     46 + if property.Name == propertyName && property.Object != nil {
     47 + result = append(result, property.Object.Data)
     48 + result = append(result, generictypes.Object{
     49 + Properties: []generictypes.Property{{
     50 + Name: propertyName,
     51 + Object: &types.Detection{
     52 + DetectorType: "object",
     53 + MatchNode: node,
     54 + Data: property.Object.Data,
     55 + },
     56 + }},
     57 + IsVirtual: true,
     58 + })
     59 + }
     60 + }
     61 + }
     62 + }
     63 + 
     64 + if objectName != "" {
     65 + result = append(result, generictypes.Object{
     66 + Properties: []generictypes.Property{{
     67 + Name: objectName,
     68 + Object: &types.Detection{
     69 + DetectorType: "object",
     70 + MatchNode: node,
     71 + Data: generictypes.Object{
     72 + Properties: []generictypes.Property{{Name: propertyName}},
     73 + IsVirtual: true,
     74 + },
     75 + },
     76 + }},
     77 + IsVirtual: true,
     78 + })
     79 + }
     80 + 
     81 + return result, nil
     82 +}
     83 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/generic_test.go
     1 +package generic_test
     2 + 
     3 +import (
     4 + "testing"
     5 + 
     6 + "github.com/bearer/bearer/new/detector/composition/ruby"
     7 + "github.com/bearer/bearer/new/detector/implementation/testhelper"
     8 +)
     9 + 
     10 +func TestDatatypeDetector(t *testing.T) {
     11 + runTest(t, "datatype", "datatype", "testdata/datatype.rb")
     12 +}
     13 + 
     14 +func runTest(t *testing.T, name string, detectorType, fileName string) {
     15 + testhelper.RunTest(t, name, ruby.New, detectorType, fileName)
     16 +}
     17 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/testdata/datatype.rb
     1 +x = {
     2 + y: {
     3 + user: { first_name: "" }
     4 + },
     5 + email: ""
     6 +}
     7 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/generic/types/types.go
    1 1  package types
    2 2   
    3  -import "github.com/bearer/bearer/new/detector/types"
     3 +import (
     4 + "github.com/bearer/bearer/new/detector/types"
     5 + "github.com/bearer/bearer/new/language/tree"
     6 +)
    4 7   
    5 8  type Object struct {
    6  - Name string
    7  - Properties []*types.Detection
     9 + Properties []Property
     10 + // IsVirtual describes whether this object actually exists, or has
     11 + // been detected as part of a variable name
     12 + IsVirtual bool
    8 13  }
    9 14   
    10 15  type Property struct {
    11  - Name string
     16 + Name string
     17 + Node *tree.Node
     18 + Object *types.Detection
    12 19  }
    13 20   
    14 21  type String struct {
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptFileTypes-file_type_jsx
    1 1  - position: "1:7"
    2 2   content: 'user: { email: string } = { email: "[email protected]" }'
    3 3   data:
    4  - name: user
    5 4   properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: email
     5 + - name: user
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: email
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
    10 17   
    11 18   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptFileTypes-file_type_tsx
    1 1  - position: "1:7"
    2 2   content: 'user: { email: string } = { email: "[email protected]" }'
    3 3   data:
    4  - name: user
    5 4   properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: email
     5 + - name: user
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: email
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
    10 17   
    11 18   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptObjectDetector-object_class
     1 +- position: "1:1"
     2 + content: |-
     3 + class User {
     4 + constructor (a, b) {}
     5 + 
     6 + x() {}
     7 + y() {}
     8 + }
     9 + data:
     10 + properties:
     11 + - name: User
     12 + node: null
     13 + object:
     14 + detectortype: object
     15 + matchnode: {}
     16 + data:
     17 + properties:
     18 + - name: x
     19 + node: null
     20 + object: null
     21 + - name: "y"
     22 + node: null
     23 + object: null
     24 + isvirtual: false
     25 + isvirtual: false
     26 + 
     27 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptObjectDetector-object_object
     1 +- position: "1:5"
     2 + content: 'nested = { "a": 123 }'
     3 + data:
     4 + properties:
     5 + - name: nested
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: a
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
     17 +- position: "3:6"
     18 + content: |-
     19 + {
     20 + x: { n: nested },
     21 + y: { b: 4 }
     22 + }
     23 + data:
     24 + properties:
     25 + - name: x
     26 + node: {}
     27 + object:
     28 + detectortype: object
     29 + matchnode: {}
     30 + data:
     31 + properties:
     32 + - name: "n"
     33 + node: {}
     34 + object:
     35 + detectortype: object
     36 + matchnode: {}
     37 + data:
     38 + properties:
     39 + - name: nested
     40 + node: {}
     41 + object:
     42 + detectortype: object
     43 + matchnode: {}
     44 + data:
     45 + properties:
     46 + - name: a
     47 + node: {}
     48 + object: null
     49 + isvirtual: false
     50 + isvirtual: true
     51 + - name: "n"
     52 + node: {}
     53 + object:
     54 + detectortype: object
     55 + matchnode: {}
     56 + data:
     57 + properties:
     58 + - name: a
     59 + node: {}
     60 + object: null
     61 + isvirtual: false
     62 + isvirtual: false
     63 + - name: "y"
     64 + node: {}
     65 + object:
     66 + detectortype: object
     67 + matchnode: {}
     68 + data:
     69 + properties:
     70 + - name: b
     71 + node: {}
     72 + object: null
     73 + isvirtual: false
     74 + isvirtual: false
     75 + 
     76 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptObjectDetector-object_projection
     1 +- position: "1:5"
     2 + content: 'obj = { x: { a: { i: 3 } }, y: 4 }'
     3 + data:
     4 + properties:
     5 + - name: obj
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: x
     13 + node: {}
     14 + object:
     15 + detectortype: object
     16 + matchnode: {}
     17 + data:
     18 + properties:
     19 + - name: a
     20 + node: {}
     21 + object:
     22 + detectortype: object
     23 + matchnode: {}
     24 + data:
     25 + properties:
     26 + - name: i
     27 + node: {}
     28 + object: null
     29 + isvirtual: false
     30 + isvirtual: false
     31 + - name: "y"
     32 + node: {}
     33 + object: null
     34 + isvirtual: false
     35 + isvirtual: true
     36 +- position: "4:1"
     37 + content: obj.x
     38 + data:
     39 + properties:
     40 + - name: a
     41 + node: {}
     42 + object:
     43 + detectortype: object
     44 + matchnode: {}
     45 + data:
     46 + properties:
     47 + - name: i
     48 + node: {}
     49 + object: null
     50 + isvirtual: false
     51 + isvirtual: false
     52 +- position: "4:1"
     53 + content: obj.x
     54 + data:
     55 + properties:
     56 + - name: x
     57 + node: null
     58 + object:
     59 + detectortype: object
     60 + matchnode: {}
     61 + data:
     62 + properties:
     63 + - name: a
     64 + node: {}
     65 + object:
     66 + detectortype: object
     67 + matchnode: {}
     68 + data:
     69 + properties:
     70 + - name: i
     71 + node: {}
     72 + object: null
     73 + isvirtual: false
     74 + isvirtual: false
     75 + isvirtual: true
     76 +- position: "4:1"
     77 + content: obj.x
     78 + data:
     79 + properties:
     80 + - name: obj
     81 + node: null
     82 + object:
     83 + detectortype: object
     84 + matchnode: {}
     85 + data:
     86 + properties:
     87 + - name: x
     88 + node: null
     89 + object: null
     90 + isvirtual: true
     91 + isvirtual: true
     92 +- position: "5:1"
     93 + content: obj["x"].a
     94 + data:
     95 + properties:
     96 + - name: i
     97 + node: {}
     98 + object: null
     99 + isvirtual: false
     100 +- position: "5:1"
     101 + content: obj["x"].a
     102 + data:
     103 + properties:
     104 + - name: a
     105 + node: null
     106 + object:
     107 + detectortype: object
     108 + matchnode: {}
     109 + data:
     110 + properties:
     111 + - name: i
     112 + node: {}
     113 + object: null
     114 + isvirtual: false
     115 + isvirtual: true
     116 +- position: "5:1"
     117 + content: obj["x"].a
     118 + data:
     119 + properties:
     120 + - name: x
     121 + node: null
     122 + object:
     123 + detectortype: object
     124 + matchnode: {}
     125 + data:
     126 + properties:
     127 + - name: a
     128 + node: null
     129 + object: null
     130 + isvirtual: true
     131 + isvirtual: true
     132 +- position: "8:1"
     133 + content: obj.z
     134 + data:
     135 + properties:
     136 + - name: obj
     137 + node: null
     138 + object:
     139 + detectortype: object
     140 + matchnode: {}
     141 + data:
     142 + properties:
     143 + - name: z
     144 + node: null
     145 + object: null
     146 + isvirtual: true
     147 + isvirtual: true
     148 +- position: "9:1"
     149 + content: obj["w"]
     150 + data:
     151 + properties:
     152 + - name: obj
     153 + node: null
     154 + object:
     155 + detectortype: object
     156 + matchnode: {}
     157 + data:
     158 + properties:
     159 + - name: w
     160 + node: null
     161 + object: null
     162 + isvirtual: true
     163 + isvirtual: true
     164 +- position: "12:1"
     165 + content: 'obj.x({ email: " " }, { first_name: "" })'
     166 + data:
     167 + properties:
     168 + - name: ""
     169 + node: null
     170 + object:
     171 + detectortype: object
     172 + matchnode: {}
     173 + data:
     174 + properties:
     175 + - name: a
     176 + node: {}
     177 + object:
     178 + detectortype: object
     179 + matchnode: {}
     180 + data:
     181 + properties:
     182 + - name: i
     183 + node: {}
     184 + object: null
     185 + isvirtual: false
     186 + isvirtual: false
     187 + - name: ""
     188 + node: null
     189 + object:
     190 + detectortype: object
     191 + matchnode: {}
     192 + data:
     193 + properties:
     194 + - name: x
     195 + node: null
     196 + object:
     197 + detectortype: object
     198 + matchnode: {}
     199 + data:
     200 + properties:
     201 + - name: a
     202 + node: {}
     203 + object:
     204 + detectortype: object
     205 + matchnode: {}
     206 + data:
     207 + properties:
     208 + - name: i
     209 + node: {}
     210 + object: null
     211 + isvirtual: false
     212 + isvirtual: false
     213 + isvirtual: true
     214 + - name: ""
     215 + node: null
     216 + object:
     217 + detectortype: object
     218 + matchnode: {}
     219 + data:
     220 + properties:
     221 + - name: obj
     222 + node: null
     223 + object:
     224 + detectortype: object
     225 + matchnode: {}
     226 + data:
     227 + properties:
     228 + - name: x
     229 + node: null
     230 + object: null
     231 + isvirtual: true
     232 + isvirtual: true
     233 + isvirtual: true
     234 +- position: "13:1"
     235 + content: 'obj.x({ email: " " }, { first_name: "" }).a'
     236 + data:
     237 + properties:
     238 + - name: x
     239 + node: null
     240 + object:
     241 + detectortype: object
     242 + matchnode: {}
     243 + data:
     244 + properties:
     245 + - name: a
     246 + node: null
     247 + object: null
     248 + isvirtual: true
     249 + isvirtual: true
     250 +- position: "16:7"
     251 + content: x
     252 + data:
     253 + properties:
     254 + - name: a
     255 + node: {}
     256 + object:
     257 + detectortype: object
     258 + matchnode: {}
     259 + data:
     260 + properties:
     261 + - name: i
     262 + node: {}
     263 + object: null
     264 + isvirtual: false
     265 + isvirtual: false
     266 +- position: "16:7"
     267 + content: x
     268 + data:
     269 + properties:
     270 + - name: x
     271 + node: null
     272 + object:
     273 + detectortype: object
     274 + matchnode: {}
     275 + data:
     276 + properties:
     277 + - name: a
     278 + node: {}
     279 + object:
     280 + detectortype: object
     281 + matchnode: {}
     282 + data:
     283 + properties:
     284 + - name: i
     285 + node: {}
     286 + object: null
     287 + isvirtual: false
     288 + isvirtual: false
     289 + isvirtual: true
     290 +- position: "16:7"
     291 + content: x
     292 + data:
     293 + properties:
     294 + - name: obj
     295 + node: null
     296 + object:
     297 + detectortype: object
     298 + matchnode: {}
     299 + data:
     300 + properties:
     301 + - name: x
     302 + node: null
     303 + object: null
     304 + isvirtual: true
     305 + isvirtual: true
     306 + 
     307 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptTypes-typed_object_const
    1 1  - position: "1:7"
    2 2   content: 'user: { email: string } = { email: "[email protected]" }'
    3 3   data:
    4  - name: user
    5 4   properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: email
     5 + - name: user
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: email
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
    10 17   
    11 18   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptTypes-typed_object_let
    1 1  - position: "1:5"
    2 2   content: 'user: { email: string } = { email: "[email protected]" }'
    3 3   data:
    4  - name: user
    5 4   properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: email
     5 + - name: user
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: email
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
    10 17   
    11 18   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/.snapshots/TestJavascriptTypes-typed_object_var
    1 1  - position: "1:5"
    2 2   content: 'user: { email: string } = { email: "[email protected]" }'
    3 3   data:
    4  - name: user
    5 4   properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: email
     5 + - name: user
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: email
     13 + node: {}
     14 + object: null
     15 + isvirtual: false
     16 + isvirtual: true
    10 17   
    11 18   
  • ■ ■ ■ ■ ■
    new/detector/implementation/javascript/javascript_test.go
    skipped 6 lines
    7 7   "github.com/bearer/bearer/new/detector/implementation/testhelper"
    8 8  )
    9 9   
     10 +func TestJavascriptObjectDetector(t *testing.T) {
     11 + runTest(t, "object_class", "object", "testdata/object_class.js")
     12 + runTest(t, "object_object", "object", "testdata/object_object.js")
     13 + runTest(t, "object_projection", "object", "testdata/object_projection.js")
     14 +}
     15 + 
    10 16  func TestJavascriptStringDetector(t *testing.T) {
    11 17   runTest(t, "string_literal", "string", "testdata/string_literal.js")
    12 18   runTest(t, "string_non_literal", "string", "testdata/string_non_literal.js")
    13  - runTest(t, "typed_object", "object", "testdata/typed_object.ts")
    14 19  }
    15 20   
    16 21  func TestJavascriptTypes(t *testing.T) {
    skipped 14 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/object/object.go
    skipped 6 lines
    7 7   "github.com/bearer/bearer/new/language/tree"
    8 8   "github.com/bearer/bearer/pkg/util/stringutil"
    9 9   
     10 + "github.com/bearer/bearer/new/detector/implementation/generic"
    10 11   generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    11 12   languagetypes "github.com/bearer/bearer/new/language/types"
    12 13  )
    13 14   
    14 15  type objectDetector struct {
    15 16   types.DetectorBase
    16  - // Gathering properties
     17 + // Base
    17 18   objectPairQuery *tree.Query
     19 + classQuery *tree.Query
    18 20   // Naming
    19 21   assignmentQuery *tree.Query
    20  - parentPairQuery *tree.Query
    21  - // Variables
    22  - variableDeclarationQuery *tree.Query
    23  - objectDeconstructionQuery *tree.Query
    24  - // class
    25  - classNameQuery *tree.Query
    26  - constructorQuery *tree.Query
    27  - // properties
     22 + // Projection
    28 23   memberExpressionQuery *tree.Query
    29 24   subscriptExpressionQuery *tree.Query
     25 + callQuery *tree.Query
     26 + // FIXME: what to do with this?
     27 + objectDeconstructionQuery *tree.Query
    30 28  }
    31 29   
    32 30  func New(lang languagetypes.Language) (types.Detector, error) {
    33 31   // { first_name: ..., ... }
    34  - objectPairQuery, err := lang.CompileQuery(`(object (pair) @pair) @root`)
     32 + objectPairQuery, err := lang.CompileQuery(`(object (pair key: (_) @key value: (_) @value) @pair) @root`)
    35 33   if err != nil {
    36 34   return nil, fmt.Errorf("error compiling object pair query: %s", err)
    37 35   }
    38 36   
     37 + // user = <object>
    39 38   // const user = <object>
    40 39   // var user = <object>
    41 40   // let user = <object>
    42  - variableDeclarationQuery, err := lang.CompileQuery(`(variable_declarator name: (identifier) @name value: (_) @value) @root`)
     41 + assignmentQuery, err := lang.CompileQuery(`[
     42 + (assignment_expression left: (identifier) @name right: (_) @value)
     43 + (variable_declarator name: (identifier) @name value: (_) @value)
     44 + ] @root`)
    43 45   if err != nil {
    44 46   return nil, fmt.Errorf("error compiling assignment query: %s", err)
    45 47   }
    skipped 1 lines
    47 49   // const { user } = <object>
    48 50   // let { user } = <object>
    49 51   // var { user } = <object>
    50  - objectDeconstructionQuery, err := lang.CompileQuery(`(variable_declarator name:(object_pattern (shorthand_property_identifier_pattern) @match ) value: (_) @value) @root`)
     52 + objectDeconstructionQuery, err := lang.CompileQuery(`(variable_declarator name: (object_pattern (shorthand_property_identifier_pattern) @match) value: (_) @value) @root`)
    51 53   if err != nil {
    52 54   return nil, fmt.Errorf("error compiling object deconstruction query: %s", err)
    53 55   }
    54 56   
    55  - // user = <object>
    56  - assignmentQuery, err := lang.CompileQuery(`(assignment_expression left: (identifier) @left right: (_) @right) @root`)
    57  - if err != nil {
    58  - return nil, fmt.Errorf("error compiling assignment query: %s", err)
    59  - }
    60  - 
    61  - // { user: <object> }
    62  - parentPairQuery, err := lang.CompileQuery(`(pair key: (_) @key value: (_) @value) @root`)
    63  - if err != nil {
    64  - return nil, fmt.Errorf("error compiling parent pair query: %s", err)
    65  - }
    66  - 
    67  - // new User()
    68  - constructorQuery, err := lang.CompileQuery(`(new_expression constructor: (identifier) @name) @root`)
    69  - if err != nil {
    70  - return nil, fmt.Errorf("error compiling class name query: %s", err)
    71  - }
    72  - 
    73  - classNameQuery, err := lang.CompileQuery(`(class_declaration name: (type_identifier) @name) @root`)
     57 + // class User {
     58 + // constructor(name, surname) {}
     59 + // GetName() {}
     60 + // }
     61 + classQuery, err := lang.CompileQuery(`
     62 + (class_declaration
     63 + name: (type_identifier) @class_name
     64 + body: (class_body
     65 + (method_definition name: (property_identifier) @method_name (formal_parameters) @params)
     66 + )
     67 + ) @root`)
    74 68   if err != nil {
    75  - return nil, fmt.Errorf("error compiling class name query: %s", err)
     69 + return nil, fmt.Errorf("error compiling class query: %s", err)
    76 70   }
    77 71   
    78 72   // user.name
    skipped 8 lines
    87 81   return nil, fmt.Errorf("error compiling subscript expression query %s", err)
    88 82   }
    89 83   
     84 + callQuery, err := lang.CompileQuery(`(call_expression function: (_) @function) @root`)
     85 + if err != nil {
     86 + return nil, fmt.Errorf("error compiling call query: %s", err)
     87 + }
     88 + 
    90 89   return &objectDetector{
    91 90   objectPairQuery: objectPairQuery,
    92 91   assignmentQuery: assignmentQuery,
    93  - variableDeclarationQuery: variableDeclarationQuery,
    94 92   objectDeconstructionQuery: objectDeconstructionQuery,
    95  - parentPairQuery: parentPairQuery,
    96  - constructorQuery: constructorQuery,
    97  - classNameQuery: classNameQuery,
     93 + classQuery: classQuery,
    98 94   memberExpressionQuery: memberExpressionQuery,
    99 95   subscriptExpressionQuery: subscriptExpressionQuery,
     96 + callQuery: callQuery,
    100 97   }, nil
    101 98  }
    102 99   
    skipped 9 lines
    112 109   node *tree.Node,
    113 110   evaluator types.Evaluator,
    114 111  ) ([]interface{}, error) {
    115  - detections, err := detector.getobject(node, evaluator)
     112 + detections, err := detector.getObject(node, evaluator)
    116 113   if len(detections) != 0 || err != nil {
    117 114   return detections, err
    118 115   }
    119 116   
    120  - detections, err = detector.getAssigment(node, evaluator)
    121  - if len(detections) != 0 || err != nil {
    122  - return detections, err
    123  - }
    124  - 
    125  - detections, err = detector.getVariableDeclaration(node, evaluator)
    126  - if len(detections) != 0 || err != nil {
    127  - return detections, err
    128  - }
    129  - 
    130  - detections, err = detector.getObjectDeconstruction(node, evaluator)
     117 + detections, err = detector.getAssignment(node, evaluator)
    131 118   if len(detections) != 0 || err != nil {
    132 119   return detections, err
    133 120   }
    skipped 3 lines
    137 124   return detections, err
    138 125   }
    139 126   
    140  - detections, err = detector.getConstructor(node, evaluator)
    141  - if len(detections) != 0 || err != nil {
    142  - return detections, err
    143  - }
    144  - 
    145  - detections, err = detector.getProperties(node, evaluator)
    146  - if len(detections) != 0 || err != nil {
    147  - return detections, err
    148  - }
    149  - 
    150  - return detector.nameParentPairObject(node, evaluator)
     127 + return detector.getProjections(node, evaluator)
    151 128  }
    152 129   
    153  -func (detector *objectDetector) getobject(
     130 +func (detector *objectDetector) getObject(
    154 131   node *tree.Node,
    155 132   evaluator types.Evaluator,
    156 133  ) ([]interface{}, error) {
    157 134   results, err := detector.objectPairQuery.MatchAt(node)
    158  - if err != nil {
     135 + if len(results) == 0 || err != nil {
    159 136   return nil, err
    160 137   }
    161 138   
    162  - if len(results) == 0 {
    163  - return nil, nil
    164  - }
    165  - 
    166  - var properties []*types.Detection
     139 + var properties []generictypes.Property
    167 140   for _, result := range results {
    168  - nodeProperties, err := evaluator.ForNode(result["pair"], "property", false)
     141 + var name string
     142 + key := result["key"]
     143 + 
     144 + switch key.Type() {
     145 + case "string": // {"user": "admin_user"}
     146 + name = stringutil.StripQuotes(key.Content())
     147 + case "property_identifier": // { user: "admin_user"}
     148 + name = key.Content()
     149 + }
     150 + 
     151 + if name == "" {
     152 + continue
     153 + }
     154 + 
     155 + propertyObjects, err := evaluator.ForTree(result["value"], "object", true)
    169 156   if err != nil {
    170 157   return nil, err
    171 158   }
    172 159   
    173  - properties = append(properties, nodeProperties...)
     160 + pairNode := result["pair"]
     161 + 
     162 + if len(propertyObjects) == 0 {
     163 + properties = append(properties, generictypes.Property{
     164 + Name: name,
     165 + Node: pairNode,
     166 + })
     167 + 
     168 + continue
     169 + }
     170 + 
     171 + for _, propertyObject := range propertyObjects {
     172 + properties = append(properties, generictypes.Property{
     173 + Name: name,
     174 + Node: pairNode,
     175 + Object: propertyObject,
     176 + })
     177 + }
    174 178   }
    175 179   
    176 180   return []interface{}{generictypes.Object{Properties: properties}}, nil
    177 181  }
    178 182   
    179  -func (detector *objectDetector) getAssigment(
     183 +func (detector *objectDetector) getAssignment(
    180 184   node *tree.Node,
    181 185   evaluator types.Evaluator,
    182 186  ) ([]interface{}, error) {
    skipped 2 lines
    185 189   return nil, err
    186 190   }
    187 191   
    188  - objects, err := evaluator.ForNode(result["right"], "object", true)
     192 + valueObjects, err := generic.GetNonVirtualObjects(evaluator, result["value"])
    189 193   if err != nil {
    190 194   return nil, err
    191 195   }
    192 196   
    193  - var detections []interface{}
    194  - for _, object := range objects {
    195  - objectData := object.Data.(generictypes.Object)
    196  - 
    197  - if objectData.Name == "" {
    198  - detections = append(detections, generictypes.Object{
    199  - Name: result["left"].Content(),
    200  - Properties: objectData.Properties,
    201  - })
    202  - }
     197 + var objects []interface{}
     198 + for _, object := range valueObjects {
     199 + objects = append(objects, generictypes.Object{
     200 + IsVirtual: true,
     201 + Properties: []generictypes.Property{{
     202 + Name: result["name"].Content(),
     203 + Node: node,
     204 + Object: object,
     205 + }},
     206 + })
    203 207   }
    204 208   
    205  - return detections, nil
     209 + return objects, nil
    206 210  }
    207 211   
    208 212  func (detector *objectDetector) getClass(node *tree.Node, evaluator types.Evaluator) ([]interface{}, error) {
    209  - result, err := detector.classNameQuery.MatchOnceAt(node)
    210  - if result == nil || err != nil {
     213 + results, err := detector.classQuery.MatchAt(node)
     214 + if len(results) == 0 || err != nil {
    211 215   return nil, err
    212 216   }
    213 217   
    214  - data := generictypes.Object{
    215  - Name: result["name"].Content(),
    216  - Properties: []*types.Detection{},
    217  - }
     218 + className := results[0]["class_name"].Content()
    218 219   
    219  - body := node.ChildByFieldName("body")
     220 + var properties []generictypes.Property
     221 + for _, result := range results {
     222 + methodName := result["method_name"].Content()
     223 + if methodName == "constructor" {
     224 + params := result["params"]
    220 225   
    221  - for i := 0; i < body.ChildCount(); i++ {
    222  - detections, err := evaluator.ForNode(body.Child(i), "property", true)
    223  - if err != nil {
    224  - return nil, err
    225  - }
    226  - data.Properties = append(data.Properties, detections...)
    227  - }
     226 + for i := 0; i < params.ChildCount(); i++ {
     227 + param := params.Child(i)
     228 + if param.Type() != "identifier" {
     229 + continue
     230 + }
    228 231   
    229  - return []interface{}{data}, nil
    230  -}
    231  - 
    232  -func (detector *objectDetector) getConstructor(node *tree.Node, evaluator types.Evaluator) ([]interface{}, error) {
    233  - result, err := detector.constructorQuery.MatchOnceAt(node)
    234  - if result == nil || err != nil {
    235  - return nil, err
    236  - }
    237  - 
    238  - data := generictypes.Object{
    239  - Name: result["name"].Content(),
    240  - }
    241  - 
    242  - return []interface{}{data}, nil
    243  -}
    244  - 
    245  -func (detector *objectDetector) nameParentPairObject(
    246  - node *tree.Node,
    247  - evaluator types.Evaluator,
    248  -) ([]interface{}, error) {
    249  - result, err := detector.parentPairQuery.MatchOnceAt(node)
    250  - if result == nil || err != nil {
    251  - return nil, err
    252  - }
    253  - 
    254  - objects, err := evaluator.ForNode(result["value"], "object", true)
    255  - if err != nil {
    256  - return nil, err
    257  - }
    258  - 
    259  - var detections []interface{}
    260  - for _, object := range objects {
    261  - objectData := object.Data.(generictypes.Object)
    262  - 
    263  - objectName := result["key"].Content()
    264  - objectNameNode := result["key"]
    265  - if objectNameNode.Type() == "string" {
    266  - objectName = stringutil.StripQuotes(objectName)
     232 + properties = append(properties, generictypes.Property{Name: param.Content()})
     233 + }
     234 + } else {
     235 + properties = append(properties, generictypes.Property{Name: methodName})
    267 236   }
    268  - 
    269  - detections = append(detections, generictypes.Object{
    270  - Name: objectName,
    271  - Properties: objectData.Properties,
    272  - },
    273  - )
    274 237   }
    275 238   
    276  - return detections, nil
     239 + return []interface{}{generictypes.Object{
     240 + Properties: []generictypes.Property{{
     241 + Name: className,
     242 + Object: &types.Detection{
     243 + DetectorType: "object",
     244 + MatchNode: node,
     245 + Data: generictypes.Object{
     246 + Properties: properties,
     247 + },
     248 + },
     249 + }},
     250 + }}, nil
    277 251  }
    278 252   
    279 253  func (detector *objectDetector) Close() {
    280 254   detector.objectPairQuery.Close()
    281 255   detector.assignmentQuery.Close()
    282  - detector.variableDeclarationQuery.Close()
    283 256   detector.objectDeconstructionQuery.Close()
    284  - detector.parentPairQuery.Close()
    285  - detector.classNameQuery.Close()
     257 + detector.classQuery.Close()
    286 258   detector.memberExpressionQuery.Close()
    287 259   detector.subscriptExpressionQuery.Close()
     260 + detector.callQuery.Close()
    288 261  }
    289 262   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/object/projection.go
     1 +package object
     2 + 
     3 +import (
     4 + "github.com/bearer/bearer/new/detector/implementation/generic"
     5 + generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
     6 + "github.com/bearer/bearer/new/detector/types"
     7 + "github.com/bearer/bearer/new/language/tree"
     8 + "github.com/bearer/bearer/pkg/util/stringutil"
     9 +)
     10 + 
     11 +func (detector *objectDetector) getProjections(
     12 + node *tree.Node,
     13 + evaluator types.Evaluator,
     14 +) ([]interface{}, error) {
     15 + objects, err := detector.getMemberExpressionProjections(node, evaluator)
     16 + if len(objects) != 0 || err != nil {
     17 + return objects, err
     18 + }
     19 + 
     20 + objects, err = detector.getSubscriptExpressionProjections(node, evaluator)
     21 + if len(objects) != 0 || err != nil {
     22 + return objects, err
     23 + }
     24 + 
     25 + objects, err = detector.getCallProjections(node, evaluator)
     26 + if len(objects) != 0 || err != nil {
     27 + return objects, err
     28 + }
     29 + 
     30 + return detector.getObjectDeconstructionProjections(node, evaluator)
     31 +}
     32 + 
     33 +func (detector *objectDetector) getMemberExpressionProjections(
     34 + node *tree.Node,
     35 + evaluator types.Evaluator,
     36 +) ([]interface{}, error) {
     37 + result, err := detector.memberExpressionQuery.MatchOnceAt(node)
     38 + if result == nil || err != nil {
     39 + return nil, err
     40 + }
     41 + 
     42 + objectNode, isPropertyAccess := getProjectedObject(result["object"])
     43 + 
     44 + objects, err := generic.ProjectObject(
     45 + node,
     46 + evaluator,
     47 + objectNode,
     48 + getObjectName(objectNode),
     49 + result["property"].Content(),
     50 + isPropertyAccess,
     51 + )
     52 + if err != nil {
     53 + return nil, err
     54 + }
     55 + 
     56 + return objects, nil
     57 +}
     58 + 
     59 +func (detector *objectDetector) getSubscriptExpressionProjections(
     60 + node *tree.Node,
     61 + evaluator types.Evaluator,
     62 +) ([]interface{}, error) {
     63 + result, err := detector.subscriptExpressionQuery.MatchOnceAt(node)
     64 + if result == nil || err != nil {
     65 + return nil, err
     66 + }
     67 + 
     68 + objectNode, isPropertyAccess := getProjectedObject(result["object"])
     69 + propertyName := getSubscriptProperty(result["root"])
     70 + if propertyName == "" {
     71 + return nil, nil
     72 + }
     73 + 
     74 + objects, err := generic.ProjectObject(
     75 + node,
     76 + evaluator,
     77 + objectNode,
     78 + getObjectName(objectNode),
     79 + propertyName,
     80 + isPropertyAccess,
     81 + )
     82 + if err != nil {
     83 + return nil, err
     84 + }
     85 + 
     86 + return objects, nil
     87 +}
     88 + 
     89 +func (detector *objectDetector) getCallProjections(
     90 + node *tree.Node,
     91 + evaluator types.Evaluator,
     92 +) ([]interface{}, error) {
     93 + result, err := detector.callQuery.MatchOnceAt(node)
     94 + if result == nil || err != nil {
     95 + return nil, err
     96 + }
     97 + 
     98 + var properties []generictypes.Property
     99 + 
     100 + functionDetections, err := evaluator.ForTree(result["function"], "object", true)
     101 + if len(functionDetections) == 0 || err != nil {
     102 + return nil, err
     103 + }
     104 + 
     105 + for _, detection := range functionDetections {
     106 + properties = append(properties, generictypes.Property{Object: detection})
     107 + }
     108 + 
     109 + return []interface{}{generictypes.Object{Properties: properties, IsVirtual: true}}, nil
     110 +}
     111 + 
     112 +func (detector *objectDetector) getObjectDeconstructionProjections(
     113 + node *tree.Node,
     114 + evaluator types.Evaluator,
     115 +) ([]interface{}, error) {
     116 + result, err := detector.objectDeconstructionQuery.MatchOnceAt(node)
     117 + if result == nil || err != nil {
     118 + return nil, err
     119 + }
     120 + 
     121 + objectNode := result["value"]
     122 + propertyName := result["match"].Content()
     123 + if propertyName == "" {
     124 + return nil, nil
     125 + }
     126 + 
     127 + objects, err := generic.ProjectObject(
     128 + node,
     129 + evaluator,
     130 + objectNode,
     131 + getObjectName(objectNode),
     132 + propertyName,
     133 + true,
     134 + )
     135 + if err != nil {
     136 + return nil, err
     137 + }
     138 + 
     139 + return objects, nil
     140 +}
     141 + 
     142 +func getObjectName(objectNode *tree.Node) string {
     143 + // user.name or user["name"]
     144 + if objectNode.Type() == "identifier" {
     145 + return objectNode.Content()
     146 + }
     147 + 
     148 + // address.city.zip or address.city["zip"]
     149 + if objectNode.Type() == "member_expression" {
     150 + return objectNode.ChildByFieldName("property").Content()
     151 + }
     152 + 
     153 + // address["city"].zip or address["city"]["zip"]
     154 + if objectNode.Type() == "subscript_expression" {
     155 + return getSubscriptProperty(objectNode)
     156 + }
     157 + 
     158 + return ""
     159 +}
     160 + 
     161 +func getSubscriptProperty(node *tree.Node) string {
     162 + indexNode := node.ChildByFieldName("index")
     163 + if indexNode.Type() == "string" {
     164 + return stringutil.StripQuotes(indexNode.Content())
     165 + }
     166 + 
     167 + return ""
     168 +}
     169 + 
     170 +func getProjectedObject(objectNode *tree.Node) (*tree.Node, bool) {
     171 + if objectNode.Type() == "call_expression" {
     172 + return objectNode.ChildByFieldName("function"), false
     173 + }
     174 + 
     175 + return objectNode, true
     176 +}
     177 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/object/property.go
    1  -package object
    2  - 
    3  -import (
    4  - "github.com/bearer/bearer/new/detector/types"
    5  - "github.com/bearer/bearer/new/language/tree"
    6  - 
    7  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    8  -)
    9  - 
    10  -func (detector *objectDetector) getProperties(
    11  - node *tree.Node,
    12  - evaluator types.Evaluator,
    13  -) ([]interface{}, error) {
    14  - var objectParent *tree.Node
    15  - var objectProperty *tree.Node
    16  - 
    17  - results, err := detector.memberExpressionQuery.MatchAt(node)
    18  - if err != nil {
    19  - return nil, err
    20  - }
    21  - 
    22  - for _, result := range results {
    23  - // user.name
    24  - if result["object"].Type() == "identifier" {
    25  - objectParent = result["object"]
    26  - objectProperty = result["property"]
    27  - }
    28  - 
    29  - // address.city.zip
    30  - if result["object"].Type() == "member_expression" {
    31  - memberExpressionProperty := extractFromMemberExpression(result["object"])
    32  - 
    33  - if memberExpressionProperty != nil {
    34  - objectParent = memberExpressionProperty
    35  - objectProperty = result["property"]
    36  - }
    37  - }
    38  - 
    39  - // address["city"].zip
    40  - if result["object"].Type() == "subscript_expression" {
    41  - subscriptExpressionProperty := extractFromSubscriptExpression(result["object"])
    42  - 
    43  - if subscriptExpressionProperty != nil {
    44  - objectParent = subscriptExpressionProperty
    45  - objectProperty = result["property"]
    46  - }
    47  - }
    48  - }
    49  - 
    50  - results, err = detector.subscriptExpressionQuery.MatchAt(node)
    51  - if err != nil {
    52  - return nil, err
    53  - }
    54  - 
    55  - for _, result := range results {
    56  - // address["city"]
    57  - if result["object"].Type() == "identifier" {
    58  - objectParent = result["object"]
    59  - objectProperty = result["index"]
    60  - }
    61  - 
    62  - // address["city"]["zip"]
    63  - if result["object"].Type() == "subscript_expression" {
    64  - subscripExpressionProperty := extractFromSubscriptExpression(result["object"])
    65  - 
    66  - if subscripExpressionProperty != nil {
    67  - objectParent = subscripExpressionProperty
    68  - objectProperty = result["index"]
    69  - }
    70  - }
    71  - 
    72  - //address["city"].zip
    73  - if result["object"].Type() == "member_expression" {
    74  - memberExpressionProperty := extractFromMemberExpression(result["object"])
    75  - 
    76  - if memberExpressionProperty != nil {
    77  - objectParent = memberExpressionProperty
    78  - objectProperty = result["index"]
    79  - }
    80  - }
    81  - }
    82  - 
    83  - if objectParent != nil && objectProperty != nil {
    84  - return []interface{}{generictypes.Object{
    85  - Name: objectParent.Content(),
    86  - Properties: []*types.Detection{
    87  - {
    88  - DetectorType: detector.Name(),
    89  - MatchNode: node,
    90  - Data: generictypes.Property{
    91  - Name: objectProperty.Content(),
    92  - },
    93  - },
    94  - },
    95  - }}, nil
    96  - }
    97  - 
    98  - return nil, nil
    99  -}
    100  - 
    101  -func extractFromSubscriptExpression(node *tree.Node) *tree.Node {
    102  - property := node.ChildByFieldName("index")
    103  - arguments := node.ChildByFieldName("arguments")
    104  - 
    105  - if property != nil && property.Type() == "string" && arguments == nil {
    106  - return property
    107  - }
    108  - 
    109  - return nil
    110  -}
    111  - 
    112  -func extractFromMemberExpression(node *tree.Node) *tree.Node {
    113  - memberExpressionProperty := node.ChildByFieldName("property")
    114  - arguments := node.ChildByFieldName("arguments")
    115  - 
    116  - if memberExpressionProperty.Type() == "property_identifier" && arguments == nil {
    117  - return memberExpressionProperty
    118  - }
    119  - 
    120  - return nil
    121  -}
    122  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/object/variables.go
    1  -package object
    2  - 
    3  -import (
    4  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    5  - "github.com/bearer/bearer/new/detector/types"
    6  - "github.com/bearer/bearer/new/language/tree"
    7  - "github.com/rs/zerolog/log"
    8  -)
    9  - 
    10  -func (detector *objectDetector) getVariableDeclaration(
    11  - node *tree.Node,
    12  - evaluator types.Evaluator,
    13  -) ([]interface{}, error) {
    14  - result, err := detector.variableDeclarationQuery.MatchOnceAt(node)
    15  - if result == nil || err != nil {
    16  - return nil, err
    17  - }
    18  - 
    19  - objects, err := evaluator.ForNode(result["value"], "object", true)
    20  - if err != nil {
    21  - return nil, err
    22  - }
    23  - 
    24  - var detections []interface{}
    25  - for _, object := range objects {
    26  - objectData := object.Data.(generictypes.Object)
    27  - 
    28  - if objectData.Name == "" {
    29  - detections = append(detections, generictypes.Object{
    30  - Name: result["name"].Content(),
    31  - Properties: objectData.Properties,
    32  - },
    33  - )
    34  - }
    35  - }
    36  - 
    37  - return detections, nil
    38  -}
    39  - 
    40  -func (detector *objectDetector) getObjectDeconstruction(
    41  - node *tree.Node,
    42  - evaluator types.Evaluator,
    43  -) ([]interface{}, error) {
    44  - result, err := detector.objectDeconstructionQuery.MatchOnceAt(node)
    45  - if result == nil || err != nil {
    46  - return nil, err
    47  - }
    48  - 
    49  - objects, err := evaluator.ForNode(result["value"], "object", true)
    50  - if err != nil {
    51  - return nil, err
    52  - }
    53  - 
    54  - var detections []interface{}
    55  - 
    56  - if len(objects) != 1 {
    57  - return detections, nil
    58  - }
    59  - 
    60  - object := objects[0]
    61  - objectData := object.Data.(generictypes.Object)
    62  - 
    63  - name := result["match"]
    64  - 
    65  - for i := 0; i < name.ChildCount(); i++ {
    66  - child := name.Child(i)
    67  - if child.Type() != "shorthand_property_identifier_pattern" {
    68  - log.Debug().Msgf("child type is %s", child.Type())
    69  - continue
    70  - }
    71  - 
    72  - detections = append(detections, generictypes.Object{
    73  - Name: objectData.Name,
    74  - Properties: []*types.Detection{
    75  - {
    76  - DetectorType: detector.Name(),
    77  - MatchNode: node,
    78  - Data: generictypes.Property{
    79  - Name: child.Content(),
    80  - },
    81  - },
    82  - },
    83  - })
    84  - }
    85  - 
    86  - return detections, nil
    87  -}
    88  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/property/property.go
    1  -package property
    2  - 
    3  -import (
    4  - "fmt"
    5  - 
    6  - "github.com/bearer/bearer/new/detector/types"
    7  - "github.com/bearer/bearer/new/language/tree"
    8  - "github.com/bearer/bearer/pkg/util/stringutil"
    9  - 
    10  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    11  - languagetypes "github.com/bearer/bearer/new/language/types"
    12  -)
    13  - 
    14  -type propertyDetector struct {
    15  - types.DetectorBase
    16  - 
    17  - pairQuery *tree.Query
    18  - functionNameQuery *tree.Query
    19  - methodNameQuery *tree.Query
    20  -}
    21  - 
    22  -func New(lang languagetypes.Language) (types.Detector, error) {
    23  - // { user: "admin_user" }
    24  - pairQuery, err := lang.CompileQuery(`(pair key: (_) @key value: (_) @value) @root`)
    25  - if err != nil {
    26  - return nil, fmt.Errorf("error compiling pair query: %s", err)
    27  - }
    28  - 
    29  - // function getName(){}
    30  - functionNameQuery, err := lang.CompileQuery(`(function_declaration name: (identifier) @name) @root`)
    31  - if err != nil {
    32  - return nil, fmt.Errorf("error compiling function name query: %s", err)
    33  - }
    34  - 
    35  - // class User {
    36  - // constructor(name, surname)
    37  - // GetName()
    38  - // }
    39  - methodNameQuery, err := lang.CompileQuery(`(method_definition name: (property_identifier) @name (formal_parameters) @params ) @root`)
    40  - if err != nil {
    41  - return nil, fmt.Errorf("error compiling class method query: %s", err)
    42  - }
    43  - 
    44  - return &propertyDetector{
    45  - pairQuery: pairQuery,
    46  - functionNameQuery: functionNameQuery,
    47  - methodNameQuery: methodNameQuery,
    48  - }, nil
    49  -}
    50  - 
    51  -func (detector *propertyDetector) Name() string {
    52  - return "property"
    53  -}
    54  - 
    55  -func (detector *propertyDetector) DetectAt(
    56  - node *tree.Node,
    57  - evaluator types.Evaluator,
    58  -) ([]interface{}, error) {
    59  - // run pair query
    60  - result, err := detector.pairQuery.MatchOnceAt(node)
    61  - if err != nil {
    62  - return nil, err
    63  - }
    64  - 
    65  - if len(result) != 0 {
    66  - key := result["key"]
    67  - 
    68  - // {"user": "admin_user"}
    69  - if key.Type() == "string" {
    70  - return []interface{}{generictypes.Property{Name: stringutil.StripQuotes(result["key"].Content())}}, nil
    71  - }
    72  - 
    73  - // { user: "admin_user"}
    74  - if key.Type() == "property_identifier" {
    75  - return []interface{}{generictypes.Property{Name: result["key"].Content()}}, nil
    76  - }
    77  - }
    78  - 
    79  - // run function name query
    80  - result, err = detector.functionNameQuery.MatchOnceAt(node)
    81  - if err != nil {
    82  - return nil, err
    83  - }
    84  - if len(result) != 0 {
    85  - return []interface{}{generictypes.Property{Name: result["name"].Content()}}, nil
    86  - }
    87  - 
    88  - // run method name query
    89  - return detector.getMethod(node, evaluator)
    90  -}
    91  - 
    92  -func (detector *propertyDetector) getMethod(
    93  - node *tree.Node,
    94  - evaluator types.Evaluator,
    95  -) ([]interface{}, error) {
    96  - // run method query
    97  - result, err := detector.methodNameQuery.MatchOnceAt(node)
    98  - if err != nil || len(result) == 0 {
    99  - return nil, err
    100  - }
    101  - 
    102  - // fetch all arguments from constructor
    103  - if result["name"].Content() == "constructor" {
    104  - properties := []interface{}{}
    105  - 
    106  - params := result["params"]
    107  - 
    108  - for i := 0; i < params.ChildCount(); i++ {
    109  - param := params.Child(i)
    110  - 
    111  - if !(param.Type() == "required_parameter" && param.Child(0) != nil && param.Child(0).Type() == "identifier") {
    112  - continue
    113  - }
    114  - 
    115  - properties = append(properties, generictypes.Property{Name: param.Content()})
    116  - }
    117  - 
    118  - return properties, nil
    119  - }
    120  - 
    121  - return []interface{}{generictypes.Property{Name: result["name"].Content()}}, nil
    122  - 
    123  -}
    124  - 
    125  -func (detector *propertyDetector) Close() {
    126  - detector.pairQuery.Close()
    127  - detector.functionNameQuery.Close()
    128  - detector.methodNameQuery.Close()
    129  -}
    130  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/testdata/object_class.js
     1 +class User {
     2 + constructor (a, b) {}
     3 + 
     4 + x() {}
     5 + y() {}
     6 +}
     7 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/testdata/object_object.js
     1 +let nested = { "a": 123 }
     2 + 
     3 +call({
     4 + x: { n: nested },
     5 + y: { b: 4 }
     6 +})
     7 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/javascript/testdata/object_projection.js
     1 +let obj = { x: { a: { i: 3 } }, y: 4 }
     2 + 
     3 +// Known properties
     4 +obj.x
     5 +obj["x"].a
     6 + 
     7 +// Unknown properties
     8 +obj.z
     9 +obj["w"]
     10 + 
     11 +// Call
     12 +obj.x({ email: " " }, { first_name: "" })
     13 +obj.x({ email: " " }, { first_name: "" }).a
     14 + 
     15 +// Deconstruction
     16 +let { x } = obj
     17 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_assignment
    1  -- position: "1:1"
    2  - content: 'user = { first_name: "" }'
    3  - data:
    4  - name: user
    5  - properties:
    6  - - detectortype: property
    7  - matchnode: {}
    8  - data:
    9  - name: first_name
    10  - 
    11  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_chain
    1  -- position: "1:1"
    2  - content: '@one.two'
    3  - data:
    4  - name: one
    5  - properties:
    6  - - detectortype: object
    7  - matchnode: {}
    8  - data:
    9  - name: two
    10  -- position: "3:1"
    11  - content: three[:four]
    12  - data:
    13  - name: three
    14  - properties:
    15  - - detectortype: object
    16  - matchnode: {}
    17  - data:
    18  - name: four
    19  -- position: "5:1"
    20  - content: '@five["six"]'
    21  - data:
    22  - name: five
    23  - properties:
    24  - - detectortype: object
    25  - matchnode: {}
    26  - data:
    27  - name: six
    28  -- position: "9:1"
    29  - content: nine["ten"].eleven
    30  - data:
    31  - name: ten
    32  - properties:
    33  - - detectortype: object
    34  - matchnode: {}
    35  - data:
    36  - name: eleven
    37  - 
    38  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_class
    skipped 5 lines
    6 6   def three
    7 7   end
    8 8   
    9  - def four
     9 + def four(a, b)
    10 10   end
    11 11   end
    12 12   data:
    13  - name: A
    14 13   properties:
    15  - - detectortype: property
    16  - matchnode: {}
    17  - data:
    18  - name: one
    19  - - detectortype: property
    20  - matchnode: {}
    21  - data:
    22  - name: two
    23  - - detectortype: property
    24  - matchnode: {}
    25  - data:
    26  - name: three
    27  - - detectortype: property
    28  - matchnode: {}
    29  - data:
    30  - name: four
     14 + - name: A
     15 + node: null
     16 + object:
     17 + detectortype: object
     18 + matchnode: {}
     19 + data:
     20 + properties:
     21 + - name: one
     22 + node: {}
     23 + object: null
     24 + - name: two
     25 + node: {}
     26 + object: null
     27 + - name: three
     28 + node: {}
     29 + object: null
     30 + - name: four
     31 + node: {}
     32 + object: null
     33 + isvirtual: false
     34 + isvirtual: false
    31 35   
    32 36   
  • ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_hash
    1 1  - position: "1:1"
    2 2   content: |-
    3  - {
     3 + nested = {
    4 4   "one" => 42,
    5 5   "two" => "hi"
    6 6   }
    7 7   data:
    8  - name: ""
     8 + properties:
     9 + - name: nested
     10 + node: {}
     11 + object:
     12 + detectortype: object
     13 + matchnode: {}
     14 + data:
     15 + properties:
     16 + - name: one
     17 + node: {}
     18 + object: null
     19 + - name: two
     20 + node: {}
     21 + object: null
     22 + isvirtual: false
     23 + isvirtual: true
     24 +- position: "6:6"
     25 + content: |-
     26 + {
     27 + x: { n: nested },
     28 + y: { b: 4 }
     29 + }
     30 + data:
    9 31   properties:
    10  - - detectortype: property
    11  - matchnode: {}
    12  - data:
    13  - name: one
    14  - - detectortype: property
    15  - matchnode: {}
    16  - data:
    17  - name: two
     32 + - name: x
     33 + node: {}
     34 + object:
     35 + detectortype: object
     36 + matchnode: {}
     37 + data:
     38 + properties:
     39 + - name: "n"
     40 + node: {}
     41 + object:
     42 + detectortype: object
     43 + matchnode: {}
     44 + data:
     45 + properties:
     46 + - name: nested
     47 + node: {}
     48 + object:
     49 + detectortype: object
     50 + matchnode: {}
     51 + data:
     52 + properties:
     53 + - name: one
     54 + node: {}
     55 + object: null
     56 + - name: two
     57 + node: {}
     58 + object: null
     59 + isvirtual: false
     60 + isvirtual: true
     61 + - name: "n"
     62 + node: {}
     63 + object:
     64 + detectortype: object
     65 + matchnode: {}
     66 + data:
     67 + properties:
     68 + - name: one
     69 + node: {}
     70 + object: null
     71 + - name: two
     72 + node: {}
     73 + object: null
     74 + isvirtual: false
     75 + isvirtual: false
     76 + - name: "y"
     77 + node: {}
     78 + object:
     79 + detectortype: object
     80 + matchnode: {}
     81 + data:
     82 + properties:
     83 + - name: b
     84 + node: {}
     85 + object: null
     86 + isvirtual: false
     87 + isvirtual: false
    18 88   
    19 89   
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_parent_pair
    1  -- position: "1:1"
    2  - content: |-
    3  - {
    4  - "parent" => {
    5  - "hey" => "hi"
    6  - }
    7  - }
    8  - data:
    9  - name: ""
    10  - properties:
    11  - - detectortype: property
    12  - matchnode: {}
    13  - data:
    14  - name: parent
    15  - 
    16  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyObjectDetector-object_projection
     1 +- position: "1:5"
     2 + content: 'obj = { x: { a: { i: 3 } }, y: 4 }'
     3 + data:
     4 + properties:
     5 + - name: obj
     6 + node: {}
     7 + object:
     8 + detectortype: object
     9 + matchnode: {}
     10 + data:
     11 + properties:
     12 + - name: x
     13 + node: {}
     14 + object:
     15 + detectortype: object
     16 + matchnode: {}
     17 + data:
     18 + properties:
     19 + - name: a
     20 + node: {}
     21 + object:
     22 + detectortype: object
     23 + matchnode: {}
     24 + data:
     25 + properties:
     26 + - name: i
     27 + node: {}
     28 + object: null
     29 + isvirtual: false
     30 + isvirtual: false
     31 + - name: "y"
     32 + node: {}
     33 + object: null
     34 + isvirtual: false
     35 + isvirtual: true
     36 +- position: "4:1"
     37 + content: obj.x
     38 + data:
     39 + properties:
     40 + - name: a
     41 + node: {}
     42 + object:
     43 + detectortype: object
     44 + matchnode: {}
     45 + data:
     46 + properties:
     47 + - name: i
     48 + node: {}
     49 + object: null
     50 + isvirtual: false
     51 + isvirtual: false
     52 +- position: "4:1"
     53 + content: obj.x
     54 + data:
     55 + properties:
     56 + - name: x
     57 + node: null
     58 + object:
     59 + detectortype: object
     60 + matchnode: {}
     61 + data:
     62 + properties:
     63 + - name: a
     64 + node: {}
     65 + object:
     66 + detectortype: object
     67 + matchnode: {}
     68 + data:
     69 + properties:
     70 + - name: i
     71 + node: {}
     72 + object: null
     73 + isvirtual: false
     74 + isvirtual: false
     75 + isvirtual: true
     76 +- position: "4:1"
     77 + content: obj.x
     78 + data:
     79 + properties:
     80 + - name: obj
     81 + node: null
     82 + object:
     83 + detectortype: object
     84 + matchnode: {}
     85 + data:
     86 + properties:
     87 + - name: x
     88 + node: null
     89 + object: null
     90 + isvirtual: true
     91 + isvirtual: true
     92 +- position: "5:1"
     93 + content: obj["x"].a
     94 + data:
     95 + properties:
     96 + - name: i
     97 + node: {}
     98 + object: null
     99 + isvirtual: false
     100 +- position: "5:1"
     101 + content: obj["x"].a
     102 + data:
     103 + properties:
     104 + - name: a
     105 + node: null
     106 + object:
     107 + detectortype: object
     108 + matchnode: {}
     109 + data:
     110 + properties:
     111 + - name: i
     112 + node: {}
     113 + object: null
     114 + isvirtual: false
     115 + isvirtual: true
     116 +- position: "5:1"
     117 + content: obj["x"].a
     118 + data:
     119 + properties:
     120 + - name: x
     121 + node: null
     122 + object:
     123 + detectortype: object
     124 + matchnode: {}
     125 + data:
     126 + properties:
     127 + - name: a
     128 + node: null
     129 + object: null
     130 + isvirtual: true
     131 + isvirtual: true
     132 +- position: "8:1"
     133 + content: obj.z
     134 + data:
     135 + properties:
     136 + - name: obj
     137 + node: null
     138 + object:
     139 + detectortype: object
     140 + matchnode: {}
     141 + data:
     142 + properties:
     143 + - name: z
     144 + node: null
     145 + object: null
     146 + isvirtual: true
     147 + isvirtual: true
     148 +- position: "9:1"
     149 + content: '@myvar.x'
     150 + data:
     151 + properties:
     152 + - name: myvar
     153 + node: null
     154 + object:
     155 + detectortype: object
     156 + matchnode: {}
     157 + data:
     158 + properties:
     159 + - name: x
     160 + node: null
     161 + object: null
     162 + isvirtual: true
     163 + isvirtual: true
     164 +- position: "10:1"
     165 + content: '@myvar["w"]'
     166 + data:
     167 + properties:
     168 + - name: myvar
     169 + node: null
     170 + object:
     171 + detectortype: object
     172 + matchnode: {}
     173 + data:
     174 + properties:
     175 + - name: w
     176 + node: null
     177 + object: null
     178 + isvirtual: true
     179 + isvirtual: true
     180 + 
     181 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyPropertyDetector-property_accessor
    1  -- position: "2:3"
    2  - content: attr_accessor :one, :two
    3  - data:
    4  - name: one
    5  -- position: "2:3"
    6  - content: attr_accessor :one, :two
    7  - data:
    8  - name: two
    9  - 
    10  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyPropertyDetector-property_method
    1  -- position: "2:3"
    2  - content: |-
    3  - def one
    4  - end
    5  - data:
    6  - name: one
    7  -- position: "5:3"
    8  - content: |-
    9  - def two
    10  - end
    11  - data:
    12  - name: two
    13  - 
    14  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/.snapshots/TestRubyPropertyDetector-property_pair
    1  -- position: "2:3"
    2  - content: '"string-key" => 1'
    3  - data:
    4  - name: string-key
    5  -- position: "3:3"
    6  - content: :symbol_key => 2
    7  - data:
    8  - name: symbol_key
    9  -- position: "4:3"
    10  - content: 'hash_symbol_key: 3'
    11  - data:
    12  - name: hash_symbol_key
    13  - 
    14  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/object/object.go
    skipped 5 lines
    6 6   "github.com/bearer/bearer/new/detector/types"
    7 7   "github.com/bearer/bearer/new/language/tree"
    8 8   
     9 + "github.com/bearer/bearer/new/detector/implementation/generic"
    9 10   generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    10 11   "github.com/bearer/bearer/new/detector/implementation/ruby/common"
    11 12   languagetypes "github.com/bearer/bearer/new/language/types"
    skipped 1 lines
    13 14   
    14 15  type objectDetector struct {
    15 16   types.DetectorBase
    16  - // Gathering properties
    17  - hashPairQuery *tree.Query
     17 + // Base
     18 + hashPairQuery *tree.Query
     19 + keywordArgumentQuery *tree.Query
     20 + classQuery *tree.Query
    18 21   // Naming
    19 22   assignmentQuery *tree.Query
    20  - parentPairQuery *tree.Query
    21  - // class
    22  - classNameQuery *tree.Query
    23  - // properties
     23 + // Projection
    24 24   callsQuery *tree.Query
    25 25   elementReferenceQuery *tree.Query
    26 26  }
    27 27   
    28 28  func New(lang languagetypes.Language) (types.Detector, error) {
    29 29   // { first_name: ..., ... }
    30  - hashPairQuery, err := lang.CompileQuery(`(hash (pair) @pair) @root`)
     30 + hashPairQuery, err := lang.CompileQuery(`(hash (pair key: (_) @key value: (_) @value) @pair) @root`)
    31 31   if err != nil {
    32 32   return nil, fmt.Errorf("error compiling hash pair query: %s", err)
    33 33   }
    34 34   
     35 + // call(first_name: ...)
     36 + keywordArgumentQuery, err := lang.CompileQuery(`(argument_list (pair key: (_) @key value: (_) @value) @match) @root`)
     37 + if err != nil {
     38 + return nil, fmt.Errorf("error compiling keyword argument query: %s", err)
     39 + }
     40 + 
    35 41   // user = <object>
    36  - assignmentQuery, err := lang.CompileQuery(`(assignment left: (identifier) @left right: (_) @right) @root`)
     42 + assignmentQuery, err := lang.CompileQuery(`(assignment left: (identifier) @name right: (_) @value) @root`)
    37 43   if err != nil {
    38 44   return nil, fmt.Errorf("error compiling assignment query: %s", err)
    39  - }
    40  - // { user: <object> }
    41  - parentPairQuery, err := lang.CompileQuery(`(pair key: (_) @key value: (_) @value) @root`)
    42  - if err != nil {
    43  - return nil, fmt.Errorf("error compiling parent pair query: %s", err)
    44 45   }
    45 46   
    46 47   // class User
     48 + // attr_accessor :name
     49 + //
     50 + // def get_first_name()
     51 + // end
    47 52   // end
    48  - classNameQuery, err := lang.CompileQuery(`(class name: (constant) @name) @root`)
     53 + classQuery, err := lang.CompileQuery(`
     54 + (class name: (constant) @class_name
     55 + [
     56 + (call arguments: (argument_list (simple_symbol) @name))
     57 + (method name: (identifier) @name)
     58 + ]
     59 + ) @root`)
    49 60   if err != nil {
    50  - return nil, fmt.Errorf("error compiling class name query: %s", err)
     61 + return nil, fmt.Errorf("error compiling class query: %s", err)
    51 62   }
    52 63   
    53 64   // user.name
    skipped 10 lines
    64 75   
    65 76   return &objectDetector{
    66 77   hashPairQuery: hashPairQuery,
     78 + keywordArgumentQuery: keywordArgumentQuery,
    67 79   assignmentQuery: assignmentQuery,
    68  - parentPairQuery: parentPairQuery,
    69  - classNameQuery: classNameQuery,
     80 + classQuery: classQuery,
    70 81   callsQuery: callsQuery,
    71 82   elementReferenceQuery: elementReferenceQuery,
    72 83   }, nil
    skipped 16 lines
    89 100   return detections, err
    90 101   }
    91 102   
    92  - detections, err = detector.getAssigment(node, evaluator)
     103 + detections, err = detector.getKeywordArgument(node, evaluator)
    93 104   if len(detections) != 0 || err != nil {
    94 105   return detections, err
    95 106   }
    96 107   
    97  - detections, err = detector.getClass(node, evaluator)
     108 + detections, err = detector.getAssignment(node, evaluator)
    98 109   if len(detections) != 0 || err != nil {
    99 110   return detections, err
    100 111   }
    101 112   
    102  - detections, err = detector.getProperties(node, evaluator)
     113 + detections, err = detector.getClass(node, evaluator)
    103 114   if len(detections) != 0 || err != nil {
    104 115   return detections, err
    105 116   }
    106 117   
    107  - return detector.nameParentPairObject(node, evaluator)
     118 + return detector.getProjections(node, evaluator)
    108 119  }
    109 120   
    110 121  func (detector *objectDetector) getHash(
    skipped 1 lines
    112 123   evaluator types.Evaluator,
    113 124  ) ([]interface{}, error) {
    114 125   results, err := detector.hashPairQuery.MatchAt(node)
    115  - if err != nil {
     126 + if len(results) == 0 || err != nil {
    116 127   return nil, err
    117 128   }
    118 129   
    119  - if len(results) == 0 {
    120  - return nil, nil
    121  - }
     130 + var properties []generictypes.Property
     131 + for _, result := range results {
     132 + pairNode := result["pair"]
    122 133   
    123  - var properties []*types.Detection
    124  - for _, result := range results {
    125  - nodeProperties, err := evaluator.ForNode(result["pair"], "property", false)
     134 + name := common.GetLiteralKey(result["key"])
     135 + if name == "" {
     136 + continue
     137 + }
     138 + 
     139 + propertyObjects, err := evaluator.ForTree(result["value"], "object", true)
    126 140   if err != nil {
    127 141   return nil, err
    128 142   }
    129 143   
    130  - properties = append(properties, nodeProperties...)
     144 + if len(propertyObjects) == 0 {
     145 + properties = append(properties, generictypes.Property{
     146 + Name: name,
     147 + Node: pairNode,
     148 + })
     149 + 
     150 + continue
     151 + }
     152 + 
     153 + for _, propertyObject := range propertyObjects {
     154 + properties = append(properties, generictypes.Property{
     155 + Name: name,
     156 + Node: pairNode,
     157 + Object: propertyObject,
     158 + })
     159 + }
    131 160   }
    132 161   
    133 162   return []interface{}{generictypes.Object{Properties: properties}}, nil
    134 163  }
    135 164   
    136  -func (detector *objectDetector) getAssigment(
     165 +func (detector *objectDetector) getKeywordArgument(
    137 166   node *tree.Node,
    138 167   evaluator types.Evaluator,
    139 168  ) ([]interface{}, error) {
    140  - result, err := detector.assignmentQuery.MatchOnceAt(node)
     169 + result, err := detector.keywordArgumentQuery.MatchOnceAt(node)
    141 170   if result == nil || err != nil {
    142 171   return nil, err
    143 172   }
    144 173   
    145  - objects, err := evaluator.ForNode(result["right"], "object", true)
     174 + name := common.GetLiteralKey(result["key"])
     175 + if name == "" {
     176 + return nil, nil
     177 + }
     178 + 
     179 + propertyObjects, err := evaluator.ForTree(result["value"], "object", true)
    146 180   if err != nil {
    147 181   return nil, err
    148 182   }
    149 183   
    150  - var detectionsData []interface{}
    151  - for _, object := range objects {
    152  - objectData := object.Data.(generictypes.Object)
     184 + var properties []generictypes.Property
     185 + 
     186 + if len(propertyObjects) == 0 {
     187 + properties = append(properties, generictypes.Property{
     188 + Name: name,
     189 + Node: node,
     190 + })
     191 + }
    153 192   
    154  - if objectData.Name == "" {
    155  - detectionsData = append(detectionsData, generictypes.Object{
    156  - Name: result["left"].Content(),
    157  - Properties: objectData.Properties,
    158  - })
    159  - }
     193 + for _, propertyObject := range propertyObjects {
     194 + properties = append(properties, generictypes.Property{
     195 + Name: name,
     196 + Node: node,
     197 + Object: propertyObject,
     198 + })
    160 199   }
    161 200   
    162  - return detectionsData, nil
     201 + return []interface{}{generictypes.Object{Properties: properties}}, nil
    163 202  }
    164 203   
    165  -func (detector *objectDetector) getClass(node *tree.Node, evaluator types.Evaluator) ([]interface{}, error) {
    166  - result, err := detector.classNameQuery.MatchOnceAt(node)
     204 +func (detector *objectDetector) getAssignment(
     205 + node *tree.Node,
     206 + evaluator types.Evaluator,
     207 +) ([]interface{}, error) {
     208 + result, err := detector.assignmentQuery.MatchOnceAt(node)
    167 209   if result == nil || err != nil {
    168 210   return nil, err
    169 211   }
    170 212   
    171  - data := generictypes.Object{
    172  - Name: result["name"].Content(),
    173  - Properties: []*types.Detection{},
     213 + valueObjects, err := generic.GetNonVirtualObjects(evaluator, result["value"])
     214 + if err != nil {
     215 + return nil, err
    174 216   }
    175 217   
    176  - for i := 0; i < node.ChildCount(); i++ {
    177  - detections, err := evaluator.ForNode(node.Child(i), "property", true)
    178  - if err != nil {
    179  - return nil, err
    180  - }
    181  - data.Properties = append(data.Properties, detections...)
     218 + var objects []interface{}
     219 + for _, object := range valueObjects {
     220 + objects = append(objects, generictypes.Object{
     221 + IsVirtual: true,
     222 + Properties: []generictypes.Property{{
     223 + Name: result["name"].Content(),
     224 + Node: node,
     225 + Object: object,
     226 + }},
     227 + })
    182 228   }
    183 229   
    184  - return []interface{}{data}, nil
     230 + return objects, nil
    185 231  }
    186 232   
    187  -func (detector *objectDetector) nameParentPairObject(
    188  - node *tree.Node,
    189  - evaluator types.Evaluator,
    190  -) ([]interface{}, error) {
    191  - result, err := detector.parentPairQuery.MatchOnceAt(node)
    192  - if result == nil || err != nil {
     233 +func (detector *objectDetector) getClass(node *tree.Node, evaluator types.Evaluator) ([]interface{}, error) {
     234 + results, err := detector.classQuery.MatchAt(node)
     235 + if len(results) == 0 || err != nil {
    193 236   return nil, err
    194 237   }
    195 238   
    196  - key := common.GetLiteralKey(result["key"])
    197  - if key == "" {
    198  - return nil, nil
    199  - }
     239 + className := results[0]["class_name"].Content()
    200 240   
    201  - objects, err := evaluator.ForNode(result["value"], "object", true)
    202  - if err != nil {
    203  - return nil, err
    204  - }
     241 + var properties []generictypes.Property
     242 + for _, result := range results {
     243 + nameNode := result["name"]
     244 + name := nameNode.Content()
    205 245   
    206  - var detectionsData []interface{}
    207  - for _, object := range objects {
    208  - objectData := object.Data.(generictypes.Object)
     246 + if nameNode.Type() == "simple_symbol" {
     247 + name = name[1:]
     248 + }
    209 249   
    210  - detectionsData = append(detectionsData, generictypes.Object{
    211  - Name: key,
    212  - Properties: objectData.Properties,
    213  - })
     250 + if name != "initialize" {
     251 + properties = append(properties, generictypes.Property{
     252 + Name: name,
     253 + Node: nameNode,
     254 + })
     255 + }
    214 256   }
    215 257   
    216  - return detectionsData, nil
     258 + return []interface{}{generictypes.Object{
     259 + Properties: []generictypes.Property{{
     260 + Name: className,
     261 + Object: &types.Detection{
     262 + DetectorType: "object",
     263 + MatchNode: node,
     264 + Data: generictypes.Object{
     265 + Properties: properties,
     266 + },
     267 + },
     268 + }},
     269 + }}, nil
    217 270  }
    218 271   
    219 272  func (detector *objectDetector) Close() {
    220 273   detector.hashPairQuery.Close()
     274 + detector.keywordArgumentQuery.Close()
    221 275   detector.assignmentQuery.Close()
    222  - detector.parentPairQuery.Close()
    223  - detector.classNameQuery.Close()
     276 + detector.classQuery.Close()
    224 277   detector.callsQuery.Close()
    225 278   detector.elementReferenceQuery.Close()
    226 279  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/object/projection.go
     1 +package object
     2 + 
     3 +import (
     4 + "github.com/bearer/bearer/new/detector/implementation/generic"
     5 + "github.com/bearer/bearer/new/detector/implementation/ruby/common"
     6 + "github.com/bearer/bearer/new/detector/types"
     7 + "github.com/bearer/bearer/new/language/tree"
     8 +)
     9 + 
     10 +func (detector *objectDetector) getProjections(
     11 + node *tree.Node,
     12 + evaluator types.Evaluator,
     13 +) ([]interface{}, error) {
     14 + result, err := detector.callsQuery.MatchOnceAt(node)
     15 + if err != nil {
     16 + return nil, err
     17 + }
     18 + 
     19 + if result != nil {
     20 + receiverNode := result["receiver"]
     21 + 
     22 + objects, err := generic.ProjectObject(
     23 + node,
     24 + evaluator,
     25 + receiverNode,
     26 + getObjectName(receiverNode),
     27 + result["method"].Content(),
     28 + getIsPropertyAccess(receiverNode),
     29 + )
     30 + if err != nil {
     31 + return nil, err
     32 + }
     33 + 
     34 + return objects, nil
     35 + }
     36 + 
     37 + result, err = detector.elementReferenceQuery.MatchOnceAt(node)
     38 + if err != nil {
     39 + return nil, err
     40 + }
     41 + 
     42 + if result != nil {
     43 + objectNode := result["object"]
     44 + propertyName := getElementProperty(result["root"])
     45 + if propertyName == "" {
     46 + return nil, nil
     47 + }
     48 + 
     49 + objects, err := generic.ProjectObject(
     50 + node,
     51 + evaluator,
     52 + objectNode,
     53 + getObjectName(objectNode),
     54 + propertyName,
     55 + getIsPropertyAccess(objectNode),
     56 + )
     57 + if err != nil {
     58 + return nil, err
     59 + }
     60 + 
     61 + return objects, nil
     62 + }
     63 + 
     64 + return nil, nil
     65 +}
     66 + 
     67 +func getObjectName(objectNode *tree.Node) string {
     68 + // user.name or user["name"]
     69 + if objectNode.Type() == "identifier" {
     70 + return objectNode.Content()
     71 + }
     72 + 
     73 + // @user.name or @user["name"]
     74 + if objectNode.Type() == "instance_variable" {
     75 + return objectNode.Content()[1:]
     76 + }
     77 + 
     78 + // address.city.zip or address.city["zip"]
     79 + if objectNode.Type() == "call" {
     80 + return objectNode.ChildByFieldName("method").Content()
     81 + }
     82 + 
     83 + // address["city"].zip or address["city"]["zip"]
     84 + if objectNode.Type() == "element_reference" {
     85 + return getElementProperty(objectNode)
     86 + }
     87 + 
     88 + return ""
     89 +}
     90 + 
     91 +func getElementProperty(node *tree.Node) string {
     92 + return common.GetLiteralKey(node.NamedChild(1))
     93 +}
     94 + 
     95 +func getIsPropertyAccess(objectNode *tree.Node) bool {
     96 + return objectNode.Type() != "call" || objectNode.ChildByFieldName("arguments") == nil
     97 +}
     98 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/object/properties.go
    1  -package object
    2  - 
    3  -import (
    4  - "github.com/bearer/bearer/new/detector/types"
    5  - "github.com/bearer/bearer/new/language/tree"
    6  - 
    7  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    8  - "github.com/bearer/bearer/new/detector/implementation/ruby/common"
    9  -)
    10  - 
    11  -func (detector *objectDetector) getProperties(
    12  - node *tree.Node,
    13  - evaluator types.Evaluator,
    14  -) ([]interface{}, error) {
    15  - var objectParent string
    16  - var objectProperty string
    17  - 
    18  - result, err := detector.callsQuery.MatchOnceAt(node)
    19  - if err != nil {
    20  - return nil, err
    21  - }
    22  - 
    23  - if result != nil {
    24  - // user.name
    25  - if result["receiver"].Type() == "identifier" {
    26  - objectParent = result["receiver"].Content()
    27  - objectProperty = result["method"].Content()
    28  - }
    29  - 
    30  - // @user.name
    31  - if result["receiver"].Type() == "instance_variable" {
    32  - objectParent = result["receiver"].Content()[1:]
    33  - objectProperty = result["method"].Content()
    34  - }
    35  - 
    36  - // address.city.zip
    37  - if result["receiver"].Type() == "call" {
    38  - if callProperty := extractFromCall(result["receiver"]); callProperty != nil {
    39  - objectParent = callProperty.Content()
    40  - objectProperty = result["method"].Content()
    41  - }
    42  - }
    43  - 
    44  - // address[:city].zip
    45  - if result["receiver"].Type() == "element_reference" {
    46  - elementReferenceProperty := extractFromElementReference(result["receiver"])
    47  - 
    48  - if elementReferenceProperty != nil {
    49  - objectParent = common.GetLiteralKey(elementReferenceProperty)
    50  - objectProperty = result["method"].Content()
    51  - }
    52  - }
    53  - }
    54  - 
    55  - result, err = detector.elementReferenceQuery.MatchOnceAt(node)
    56  - if err != nil {
    57  - return nil, err
    58  - }
    59  - 
    60  - if result != nil {
    61  - key := common.GetLiteralKey(result["key"])
    62  - if key == "" {
    63  - return nil, nil
    64  - }
    65  - 
    66  - // address[:city]
    67  - if result["object"].Type() == "identifier" {
    68  - objectParent = result["object"].Content()
    69  - objectProperty = key
    70  - }
    71  - 
    72  - // @address[:city]
    73  - if result["object"].Type() == "instance_variable" {
    74  - objectParent = result["object"].Content()[1:]
    75  - objectProperty = key
    76  - }
    77  - 
    78  - // address[:city][:zip].international
    79  - if result["object"].Type() == "element_reference" {
    80  - elementReferenceProperty := extractFromElementReference(result["object"])
    81  - 
    82  - if elementReferenceProperty != nil {
    83  - objectParent = common.GetLiteralKey(elementReferenceProperty)
    84  - objectProperty = key
    85  - }
    86  - }
    87  - 
    88  - // address[:city].zip
    89  - if result["object"].Type() == "call" {
    90  - callProperty := extractFromCall(result["object"])
    91  - 
    92  - if callProperty != nil {
    93  - objectParent = callProperty.Content()
    94  - objectProperty = key
    95  - }
    96  - }
    97  - }
    98  - 
    99  - if objectParent != "" && objectProperty != "" {
    100  - return []interface{}{
    101  - generictypes.Object{
    102  - Name: objectParent,
    103  - Properties: []*types.Detection{
    104  - {
    105  - DetectorType: detector.Name(),
    106  - MatchNode: node,
    107  - Data: generictypes.Property{
    108  - Name: objectProperty,
    109  - },
    110  - },
    111  - },
    112  - },
    113  - }, nil
    114  - }
    115  - 
    116  - return nil, nil
    117  -}
    118  - 
    119  -func extractFromElementReference(node *tree.Node) *tree.Node {
    120  - for i := 0; i < node.ChildCount(); i++ {
    121  - child := node.Child(i)
    122  - if child.Type() == "simple_symbol" || child.Type() == "string" {
    123  - return child
    124  - }
    125  - }
    126  - 
    127  - return nil
    128  -}
    129  - 
    130  -func extractFromCall(node *tree.Node) *tree.Node {
    131  - callProperty := node.ChildByFieldName("method")
    132  - arguments := node.ChildByFieldName("arguments")
    133  - 
    134  - if callProperty.Type() == "identifier" && arguments == nil {
    135  - return callProperty
    136  - }
    137  - 
    138  - return nil
    139  -}
    140  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/property/property.go
    1  -package property
    2  - 
    3  -import (
    4  - "fmt"
    5  - 
    6  - "github.com/bearer/bearer/new/detector/types"
    7  - "github.com/bearer/bearer/new/language/tree"
    8  - 
    9  - generictypes "github.com/bearer/bearer/new/detector/implementation/generic/types"
    10  - "github.com/bearer/bearer/new/detector/implementation/ruby/common"
    11  - languagetypes "github.com/bearer/bearer/new/language/types"
    12  -)
    13  - 
    14  -type propertyDetector struct {
    15  - types.DetectorBase
    16  - pairQuery *tree.Query
    17  - accessorQuery *tree.Query
    18  - methodNameQuery *tree.Query
    19  -}
    20  - 
    21  -func New(lang languagetypes.Language) (types.Detector, error) {
    22  - // { user: "admin_user" }
    23  - pairQuery, err := lang.CompileQuery(`(pair key: (_) @key value: (_) @value) @root`)
    24  - if err != nil {
    25  - return nil, fmt.Errorf("error compiling pair query: %s", err)
    26  - }
    27  - 
    28  - // attr_accessor :name
    29  - accessorQuery, err := lang.CompileQuery(`(call arguments: (argument_list (simple_symbol) @name ))@root`)
    30  - if err != nil {
    31  - return nil, fmt.Errorf("error compiling class accessor query: %s", err)
    32  - }
    33  - 
    34  - // def get_first_name()
    35  - // end
    36  - methodNameQuery, err := lang.CompileQuery(`(method name: (identifier) @name) @root`)
    37  - if err != nil {
    38  - return nil, fmt.Errorf("error compiling class method query: %s", err)
    39  - }
    40  - 
    41  - return &propertyDetector{
    42  - pairQuery: pairQuery,
    43  - accessorQuery: accessorQuery,
    44  - methodNameQuery: methodNameQuery,
    45  - }, nil
    46  -}
    47  - 
    48  -func (detector *propertyDetector) Name() string {
    49  - return "property"
    50  -}
    51  - 
    52  -func (detector *propertyDetector) DetectAt(
    53  - node *tree.Node,
    54  - evaluator types.Evaluator,
    55  -) ([]interface{}, error) {
    56  - // run hash pair query
    57  - result, err := detector.pairQuery.MatchOnceAt(node)
    58  - if err != nil {
    59  - return nil, err
    60  - }
    61  - 
    62  - if result != nil {
    63  - if key := common.GetLiteralKey(result["key"]); key != "" {
    64  - return []interface{}{
    65  - generictypes.Property{Name: key},
    66  - }, nil
    67  - }
    68  - }
    69  - 
    70  - // run accessor query
    71  - results, err := detector.accessorQuery.MatchAt(node)
    72  - if err != nil {
    73  - return nil, err
    74  - }
    75  - if len(results) != 0 {
    76  - properties := make([]interface{}, len(results))
    77  - 
    78  - for i, result := range results {
    79  - properties[i] = generictypes.Property{Name: result["name"].Content()[1:]}
    80  - }
    81  - 
    82  - return properties, nil
    83  - }
    84  - 
    85  - // run method name query
    86  - result, err = detector.methodNameQuery.MatchOnceAt(node)
    87  - if err != nil {
    88  - return nil, err
    89  - }
    90  - if result != nil {
    91  - return []interface{}{
    92  - generictypes.Property{Name: result["name"].Content()},
    93  - }, nil
    94  - }
    95  - 
    96  - return nil, nil
    97  -}
    98  - 
    99  -func (detector *propertyDetector) Close() {
    100  - detector.pairQuery.Close()
    101  - detector.accessorQuery.Close()
    102  - detector.methodNameQuery.Close()
    103  -}
    104  - 
  • ■ ■ ■ ■ ■
    new/detector/implementation/ruby/ruby_test.go
    skipped 7 lines
    8 8  )
    9 9   
    10 10  func TestRubyObjectDetector(t *testing.T) {
    11  - runTest(t, "object_assignment", "object", "testdata/object_assignment.rb")
    12  - runTest(t, "object_chain", "object", "testdata/object_chain.rb")
    13 11   runTest(t, "object_class", "object", "testdata/object_class.rb")
    14 12   runTest(t, "object_hash", "object", "testdata/object_hash.rb")
    15  - runTest(t, "object_parent_pair", "object", "testdata/object_parent_pair.rb")
    16  -}
    17  - 
    18  -func TestRubyPropertyDetector(t *testing.T) {
    19  - runTest(t, "property_accessor", "property", "testdata/property_accessor.rb")
    20  - runTest(t, "property_method", "property", "testdata/property_method.rb")
    21  - runTest(t, "property_pair", "property", "testdata/property_pair.rb")
     13 + runTest(t, "object_projection", "object", "testdata/object_projection.rb")
    22 14  }
    23 15   
    24 16  func TestRubyStringDetector(t *testing.T) {
    skipped 8 lines
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_assignment.rb
    1  -user = { first_name: "" }
    2  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_chain.rb
    1  -@one.two
    2  - 
    3  -three[:four]
    4  - 
    5  -@five["six"]
    6  - 
    7  -seven().eight
    8  - 
    9  -nine["ten"].eleven
    10  - 
  • ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_class.rb
    skipped 3 lines
    4 4   def three
    5 5   end
    6 6   
    7  - def four
     7 + def four(a, b)
    8 8   end
    9 9  end
    10 10   
  • ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_hash.rb
    1  -{
     1 +nested = {
    2 2   "one" => 42,
    3 3   "two" => "hi"
    4 4  }
    5 5   
     6 +call({
     7 + x: { n: nested },
     8 + y: { b: 4 }
     9 +})
     10 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_parent_pair.rb
    1  -{
    2  - "parent" => {
    3  - "hey" => "hi"
    4  - }
    5  -}
    6  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/object_projection.rb
     1 +let obj = { x: { a: { i: 3 } }, y: 4 }
     2 + 
     3 +# Known properties
     4 +obj.x
     5 +obj["x"].a
     6 + 
     7 +# Unknown properties
     8 +obj.z
     9 +@myvar.x
     10 +@myvar["w"]
     11 + 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/property_accessor.rb
    1  -class A
    2  - attr_accessor :one, :two
    3  -end
    4  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/property_method.rb
    1  -class B
    2  - def one
    3  - end
    4  - 
    5  - def two
    6  - end
    7  -end
    8  - 
  • ■ ■ ■ ■ ■ ■
    new/detector/implementation/ruby/testdata/property_pair.rb
    1  -{
    2  - "string-key" => 1,
    3  - :symbol_key => 2,
    4  - hash_symbol_key: 3,
    5  - "c" + "omplex-key" => 4
    6  -}
    7  - 
  • ■ ■ ■ ■ ■ ■
    new/language/implementation/implementation.go
    skipped 118 lines
    119 119   // if you want to get only identifier instead of required parameter ShouldSkipNode should return true
    120 120   // for required parameter
    121 121   ShouldSkipNode(node *tree.Node) bool
     122 + 
     123 + PassthroughNested(node *tree.Node) bool
    122 124  }
    123 125   
    124 126  type Scope struct {
    skipped 27 lines
  • ■ ■ ■ ■ ■ ■
    new/language/implementation/javascript/javascript.go
    skipped 35 lines
    36 36   matchNodeRegex = regexp.MustCompile(`\$<!>`)
    37 37   
    38 38   ellipsisRegex = regexp.MustCompile(`\$<\.\.\.>`)
     39 + 
     40 + passthroughMethods = []string{"JSON.parse", "JSON.stringify"}
    39 41  )
    40 42   
    41 43  type javascriptImplementation struct{}
    skipped 71 lines
    113 115   if parent.Type() == "required_parameter" {
    114 116   scope.Assign(node.Content(), node)
    115 117   
     118 + }
     119 + 
     120 + if parent.Type() == "arguments" {
     121 + callNode := parent.Parent()
     122 + callNode.UnifyWith(node)
    116 123   }
    117 124   case "property_identifier":
    118 125   parent := node.Parent()
    skipped 124 lines
    243 250   return content
    244 251  }
    245 252   
     253 +func (*javascriptImplementation) PassthroughNested(node *tree.Node) bool {
     254 + if node.Type() != "arguments" {
     255 + return false
     256 + }
     257 + 
     258 + callNode := node.Parent()
     259 + if callNode.Type() != "call_expression" {
     260 + return false
     261 + }
     262 + 
     263 + functionNode := callNode.ChildByFieldName("function")
     264 + 
     265 + var method string
     266 + var wildcardMethod string
     267 + switch functionNode.Type() {
     268 + case "identifier":
     269 + return slices.Contains(passthroughMethods, functionNode.Content())
     270 + case "member_expression":
     271 + object := functionNode.ChildByFieldName("object")
     272 + if object.Type() == "identifier" {
     273 + property := functionNode.ChildByFieldName("property").Content()
     274 + method = object.Content() + "." + property
     275 + wildcardMethod = "*." + property
     276 + }
     277 + }
     278 + 
     279 + return slices.Contains(passthroughMethods, method) || slices.Contains(passthroughMethods, wildcardMethod)
     280 +}
     281 + 
  • ■ ■ ■ ■ ■ ■
    new/language/implementation/ruby/ruby.go
    skipped 29 lines
    30 30   matchNodeRegex = regexp.MustCompile(`\$<!>`)
    31 31   
    32 32   ellipsisRegex = regexp.MustCompile(`\$<\.\.\.>`)
     33 + 
     34 + passthroughMethods = []string{"JSON.parse", "JSON.parse!", "*.to_json"}
    33 35  )
    34 36   
    35 37  type rubyImplementation struct{}
    skipped 45 lines
    81 83   (parent.Type() == "keyword_parameter" && node.Equal(parent.ChildByFieldName("name"))) ||
    82 84   (parent.Type() == "optional_parameter" && node.Equal(parent.ChildByFieldName("name"))) {
    83 85   scope.Assign(node.Content(), node)
     86 + }
     87 + 
     88 + if parent.Type() == "argument_list" {
     89 + callNode := parent.Parent()
     90 + callNode.UnifyWith(node)
    84 91   }
    85 92   case "block", "do_block":
    86 93   previousScope := scope
    skipped 175 lines
    262 269   return true
    263 270  }
    264 271   
     272 +func (*rubyImplementation) PassthroughNested(node *tree.Node) bool {
     273 + callNode := node.Parent()
     274 + if callNode.Type() != "call" {
     275 + return false
     276 + }
     277 + 
     278 + receiverNode := callNode.ChildByFieldName("receiver")
     279 + 
     280 + if node.Type() != "arguments_list" && (receiverNode == nil || !node.Equal(receiverNode)) {
     281 + return false
     282 + }
     283 + 
     284 + var receiverMethod string
     285 + var wildcardMethod string
     286 + 
     287 + if receiverNode != nil {
     288 + methodName := callNode.ChildByFieldName("method").Content()
     289 + 
     290 + if receiverNode.Type() == "identifier" {
     291 + receiverMethod = receiverNode.Content() + "." + methodName
     292 + }
     293 + 
     294 + wildcardMethod = "*." + methodName
     295 + }
     296 + 
     297 + return slices.Contains(passthroughMethods, receiverMethod) || slices.Contains(passthroughMethods, wildcardMethod)
     298 +}
     299 + 
  • ■ ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/express/hardcoded_secret/.snapshots/TestJavascriptExpressHardCodedSecret--hardcoded_secret_in_jwt.yml
    skipped 20 lines
    21 21   parent_line_number: 21
    22 22   snippet: 'jwt({ secret: secret })'
    23 23   fingerprint: 2cc672715c780e2810ceae27cfb8da28_1
     24 +low:
     25 + - rule:
     26 + cwe_ids:
     27 + - "525"
     28 + id: javascript_express_jwt_not_revoked
     29 + description: Unrevoked JWT detected.
     30 + documentation_url: https://docs.bearer.com/reference/rules/javascript_express_jwt_not_revoked
     31 + line_number: 13
     32 + filename: hardcoded_secret_in_jwt.js
     33 + parent_line_number: 13
     34 + snippet: 'expressjwt({ secret: "my-hardcoded-secret" })'
     35 + fingerprint: 939971ad1d6ff9b19ebddbb3c104e016_0
    24 36   
    25 37   
  • ■ ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/express/jwt_not_revoked.yml
    skipped 2 lines
    3 3   expressjwt($<HASH_CONTENT>)
    4 4   filters:
    5 5   - variable: HASH_CONTENT
    6  - detection: javascript_express_jwt_not_revoked_secret_datatype
     6 + detection: javascript_express_jwt_not_revoked_secret
    7 7   - not:
    8 8   variable: HASH_CONTENT
    9 9   detection: javascript_express_jwt_not_revoked_is_revoked
    10 10  languages:
    11 11   - javascript
    12 12  auxiliary:
    13  - - id: javascript_express_jwt_not_revoked_secret_datatype
     13 + - id: javascript_express_jwt_not_revoked_secret
    14 14   patterns:
    15 15   - pattern: |
    16  - { $<...>secret: $<DATA_TYPE>$<...> }
    17  - filters:
    18  - - variable: DATA_TYPE
    19  - detection: datatype
     16 + { secret: $<_> }
    20 17   - id: javascript_express_jwt_not_revoked_is_revoked
    21 18   patterns:
    22 19   - pattern: |
    23  - { $<...>isRevoked: $<_>$<...> }
     20 + { isRevoked: $<_> }
    24 21  severity: low
    25 22  metadata:
    26 23   description: "Unrevoked JWT detected."
    skipped 21 lines
  • ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/lang/file_generation/.snapshots/TestJavascriptLangFileGeneration--file_generation.yml
    skipped 4 lines
    5 5   id: javascript_lang_file_generation
    6 6   description: Sensitive data detected as part of a dynamic file generation.
    7 7   documentation_url: https://docs.bearer.com/reference/rules/javascript_lang_file_generation
    8  - line_number: 8
     8 + line_number: 9
    9 9   filename: file_generation.js
    10 10   category_groups:
    11 11   - PII
    12  - parent_line_number: 18
     12 + parent_line_number: 15
    13 13   snippet: |-
    14 14   fs.writeFile("data.csv", JSON.stringify(users), "utf-8", (err) => {
    15 15   if (err) console.log(err)
    skipped 6 lines
    22 22   id: javascript_lang_file_generation
    23 23   description: Sensitive data detected as part of a dynamic file generation.
    24 24   documentation_url: https://docs.bearer.com/reference/rules/javascript_lang_file_generation
    25  - line_number: 11
     25 + line_number: 10
    26 26   filename: file_generation.js
    27 27   category_groups:
    28 28   - PII
    29  - parent_line_number: 18
     29 + parent_line_number: 15
    30 30   snippet: |-
    31 31   fs.writeFile("data.csv", JSON.stringify(users), "utf-8", (err) => {
    32 32   if (err) console.log(err)
    33 33   else console.log("Data saved")
    34 34   })
    35 35   fingerprint: 7dc547c0485fb50177dd96e6cc520f43_1
    36  - - rule:
    37  - cwe_ids:
    38  - - "313"
    39  - id: javascript_lang_file_generation
    40  - description: Sensitive data detected as part of a dynamic file generation.
    41  - documentation_url: https://docs.bearer.com/reference/rules/javascript_lang_file_generation
    42  - line_number: 12
    43  - filename: file_generation.js
    44  - category_groups:
    45  - - PII
    46  - parent_line_number: 18
    47  - snippet: |-
    48  - fs.writeFile("data.csv", JSON.stringify(users), "utf-8", (err) => {
    49  - if (err) console.log(err)
    50  - else console.log("Data saved")
    51  - })
    52  - fingerprint: 7dc547c0485fb50177dd96e6cc520f43_2
    53 36   
    54 37   
  • ■ ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/lang/file_generation/testdata/file_generation.js
    skipped 3 lines
    4 4   return `${firstname[0]}-${surname}`.toLowerCase()
    5 5  }
    6 6   
    7  -const users = users.map((user) => {
    8  - const username = generateUsername(user.firstname, user.surname)
    9  - 
    10  - return {
    11  - email: user.email,
    12  - first_name: user.firstname,
    13  - username,
    14  - }
    15  -})
     7 +const username = generateUsername(user.firstname, user.surname)
     8 +const users = [{
     9 + email: user.email,
     10 + first_name: user.firstname,
     11 + username,
     12 +}]
    16 13   
    17 14  fs.writeFile("data.csv", JSON.stringify(users), callback)
    18 15  fs.writeFile("data.csv", JSON.stringify(users), "utf-8", (err) => {
    skipped 4 lines
  • ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/react/google_analytics/.snapshots/TestJavascriptReactGoogleAnalytics--insecure.yml
    skipped 4 lines
    5 5   id: javascript_react_google_analytics
    6 6   description: Sensitive data sent to Google Analytics detected.
    7 7   documentation_url: https://docs.bearer.com/reference/rules/javascript_react_google_analytics
    8  - line_number: 1
    9  - filename: insecure.js
    10  - category_groups:
    11  - - PII
    12  - parent_line_number: 2
    13  - snippet: |-
    14  - ReactGA.event({
    15  - category: "user",
    16  - action: "logged_in",
    17  - value: user.email,
    18  - })
    19  - fingerprint: 325d8627339fb8007772f3afafd6b47c_0
    20  - - rule:
    21  - cwe_ids:
    22  - - "201"
    23  - id: javascript_react_google_analytics
    24  - description: Sensitive data sent to Google Analytics detected.
    25  - documentation_url: https://docs.bearer.com/reference/rules/javascript_react_google_analytics
    26 8   line_number: 5
    27 9   filename: insecure.js
    28 10   category_groups:
    skipped 5 lines
    34 16   action: "logged_in",
    35 17   value: user.email,
    36 18   })
    37  - fingerprint: 325d8627339fb8007772f3afafd6b47c_1
     19 + fingerprint: 325d8627339fb8007772f3afafd6b47c_0
    38 20   
    39 21   
  • ■ ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/third_parties/airbrake/.snapshots/TestJavascriptAirbrake--datatype_in_notify.yml
    skipped 14 lines
    15 15   error: err,
    16 16   params: { user: user.ipAddress },
    17 17   })
     18 + fingerprint: c4786d86d4c3490d41817ab11de3f9af_1
     19 +medium:
     20 + - rule:
     21 + cwe_ids:
     22 + - "201"
     23 + id: javascript_third_parties_airbrake
     24 + description: Sensitive data sent to Airbrake detected.
     25 + documentation_url: https://docs.bearer.com/reference/rules/javascript_third_parties_airbrake
     26 + line_number: 9
     27 + filename: datatype_in_notify.js
     28 + category_groups:
     29 + - PII
     30 + parent_line_number: 9
     31 + snippet: airbrake.notify("user " + currentUser().emailAddress)
    18 32   fingerprint: c4786d86d4c3490d41817ab11de3f9af_0
    19 33   
    20 34   
  • ■ ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/third_parties/bugsnag.yml
    skipped 9 lines
    10 10   - variable: DATA_TYPE
    11 11   detection: datatype
    12 12   - pattern: |
    13  - $<CLIENT>.$<METHOD>($<...>$<DATA_TYPE>$<...>)
     13 + $<CLIENT>.leaveBreadcrumb($<...>$<DATA_TYPE>$<...>)
    14 14   filters:
    15 15   - variable: CLIENT
    16 16   detection: javascript_third_parties_bugsnag_client
    17  - - variable: METHOD
    18  - values:
    19  - - start
    20  - - leaveBreadcrumb
    21 17   - variable: DATA_TYPE
    22 18   detection: datatype
     19 + - pattern: |
     20 + $<CLIENT>.start($<START_DATA_TYPE>)
     21 + filters:
     22 + - variable: CLIENT
     23 + detection: javascript_third_parties_bugsnag_client
     24 + - variable: START_DATA_TYPE
     25 + detection: javascript_third_parties_bugsnag_start_data_type
    23 26  languages:
    24 27   - javascript
    25 28  auxiliary:
    skipped 7 lines
    33 36   filters:
    34 37   - variable: CLIENT
    35 38   detection: javascript_third_parties_bugsnag_client
     39 + - id: javascript_third_parties_bugsnag_start_data_type
     40 + patterns:
     41 + - pattern: $<_>.$<_>($<...>$<DATA_TYPE>$<...>)
     42 + filters:
     43 + - variable: DATA_TYPE
     44 + detection: datatype
    36 45  skip_data_types:
    37 46   - "Unique Identifier"
    38 47  metadata:
    skipped 26 lines
  • ■ ■ ■ ■ ■
    pkg/commands/process/settings/rules/javascript/third_parties/google_tag_manager/.snapshots/TestJavascriptGTM--unsecure.yml
    skipped 4 lines
    5 5   id: javascript_google_tag_manager
    6 6   description: Sensitive data sent to Google Tag Manager detected.
    7 7   documentation_url: https://docs.bearer.com/reference/rules/javascript_google_tag_manager
    8  - line_number: 1
    9  - filename: unsecure.js
    10  - category_groups:
    11  - - PII
    12  - parent_line_number: 3
    13  - snippet: |-
    14  - window.dataLayer.push({
    15  - email: user.email,
    16  - })
    17  - fingerprint: 163d1d6557cf6be6e0f1a851cf46b2a6_0
    18  - - rule:
    19  - cwe_ids:
    20  - - "201"
    21  - id: javascript_google_tag_manager
    22  - description: Sensitive data sent to Google Tag Manager detected.
    23  - documentation_url: https://docs.bearer.com/reference/rules/javascript_google_tag_manager
    24 8   line_number: 4
    25 9   filename: unsecure.js
    26 10   category_groups:
    skipped 3 lines
    30 14   window.dataLayer.push({
    31 15   email: user.email,
    32 16   })
    33  - fingerprint: 163d1d6557cf6be6e0f1a851cf46b2a6_1
     17 + fingerprint: 163d1d6557cf6be6e0f1a851cf46b2a6_0
    34 18   
    35 19   
Please wait...
Page is in error, reload to recover