Skip to content

Commit

Permalink
feat: Backport remaining framework classifiers from Bearer Rails app (#…
Browse files Browse the repository at this point in the history
…155)

* chore: Introduce an interface for framework classifiers

* chore: Switch Rails framework detection to use recipe UUIDs

* feat: Backport remaining framework classifiers from Bearer API app
  • Loading branch information
spdawson committed Nov 22, 2022
1 parent 67e0bd9 commit f7fc96a
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 24 deletions.
20 changes: 10 additions & 10 deletions pkg/classification/frameworks/frameworks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (

"github.com/bearer/curio/pkg/classification/db"
"github.com/bearer/curio/pkg/report/detections"
"github.com/bearer/curio/pkg/report/frameworks/rails"
"github.com/bearer/curio/pkg/util/classify"
)

type classifiableFramework interface{
GetTechnologyKey() string
}

type ClassifiedFramework struct {
*detections.Detection
Classification *Classification `json:"classification" yaml:"classification"`
Expand Down Expand Up @@ -59,20 +62,17 @@ func (classifier *Classifier) Classify(data detections.Detection) (*ClassifiedFr
}

var technologyKey string
switch value := data.Value.(type) {
case rails.Cache:
technologyKey = value.GetTechnologyKey()
case rails.Database:
technologyKey = value.GetTechnologyKey()
case rails.Storage:
technologyKey = value.GetTechnologyKey()
default:

value, ok := data.Value.(classifiableFramework)
if !ok {
return &ClassifiedFramework{
Detection: &data,
Classification: classification,
}, errors.New("detection is not for a framework")
}

technologyKey = value.GetTechnologyKey()

if technologyKey != "" {
for _, recipe := range classifier.config.Recipes {
if isRecipeMatch(recipe, technologyKey) {
Expand All @@ -97,5 +97,5 @@ func (classifier *Classifier) Classify(data detections.Detection) (*ClassifiedFr
}

func isRecipeMatch(recipe db.Recipe, technologyKey string) bool {
return recipe.Name == technologyKey
return recipe.UUID == technologyKey
}
157 changes: 157 additions & 0 deletions pkg/classification/frameworks/frameworks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import (

"github.com/bearer/curio/pkg/classification/frameworks"
"github.com/bearer/curio/pkg/report/detections"
"github.com/bearer/curio/pkg/report/frameworks/beego"
"github.com/bearer/curio/pkg/report/frameworks/django"
"github.com/bearer/curio/pkg/report/frameworks/dotnet"
"github.com/bearer/curio/pkg/report/frameworks/rails"
"github.com/bearer/curio/pkg/report/frameworks/spring"
"github.com/bearer/curio/pkg/report/frameworks/symfony"
"github.com/bearer/curio/pkg/report/source"
"github.com/bearer/curio/pkg/util/classify"

Expand Down Expand Up @@ -181,6 +186,158 @@ func TestFrameworks(t *testing.T) {
Want: nil,
ShouldSucceed: true,
},
{
Name: "Beego: driver name defined",
Input: detections.Detection{
Source: source.Source{
Filename: "orm.go",
Language: "Go",
LanguageType: "programming",
},
Value: beego.Database{
Name: "default",
DriverName: "mysql",
Package: "",
TypeConstant: "",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "MySQL",
RecipeUUID: "ffa70264-2b19-445d-a5c9-be82b64fe750",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
{
Name: "Beego: package defined",
Input: detections.Detection{
Source: source.Source{
Filename: "orm.go",
Language: "Go",
LanguageType: "programming",
},
Value: beego.Database{
Name: "default",
DriverName: "",
Package: "github.com/beego/beego/v2/client/orm",
TypeConstant: "DRSqlite",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "SQLite",
RecipeUUID: "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
{
Name: "Django: database match",
Input: detections.Detection{
Source: source.Source{
Filename: "orm.py",
Language: "Python",
LanguageType: "programming",
},
Value: django.Database{
Name: "default",
Engine: "django.db.backends.mysql",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "MySQL",
RecipeUUID: "ffa70264-2b19-445d-a5c9-be82b64fe750",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
{
Name: ".NET: database match",
Input: detections.Detection{
Source: source.Source{
Filename: "Startup.cs",
Language: "C#",
LanguageType: "programming",
},
Value: dotnet.DBContext{
UseDbMethodName: "UseSqlServer",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "Microsoft SQL Server",
RecipeUUID: "e4db4505-b837-4b76-9184-c3cec3b5e522",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
{
Name: "Spring: database match",
Input: detections.Detection{
Source: source.Source{
Filename: "src/main/application.properties",
Language: "Properties",
LanguageType: "config",
},
Value: spring.DataStore{
Driver: "com.mysql.jdbc.Driver",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "MySQL",
RecipeUUID: "ffa70264-2b19-445d-a5c9-be82b64fe750",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
{
Name: "Symfony: database match",
Input: detections.Detection{
Source: source.Source{
Filename: "config/packages/doctrine.yml",
Language: "YAML",
LanguageType: "config",
},
Value: symfony.Database{
Name: "production",
Driver: "oci8",
},
Type: detections.TypeFramework,
},
Want: &frameworks.Classification{
RecipeMatch: true,
RecipeName: "Oracle",
RecipeUUID: "80886e2a-ee2c-423d-98bc-0a3d743787b4",
Decision: classify.ClassificationDecision{
State: classify.Valid,
Reason: "recipe_match",
},
},
ShouldSucceed: true,
},
}

classifier := frameworks.NewDefault()
Expand Down
38 changes: 38 additions & 0 deletions pkg/report/frameworks/beego/beego.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,41 @@ type Database struct {
Package string `json:"package" yaml:"package"`
TypeConstant string `json:"type_constant" yaml:"type_constant"`
}

func (value Database) GetTechnologyKey() string {
if value.Package != "" {
return technologyForDriverLib(value.Package, value.TypeConstant)
}

return technologyForDriverName(value.DriverName)
}

func technologyForDriverLib(packageName string, typeConstant string) string {
if packageName != "github.com/beego/beego/v2/client/orm" {
return "unidentified_data_store"
}

switch typeConstant {
case "DRMySQL", "DR_MySQL":
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "DRPostgres", "DR_Postgres":
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "DRSqlite", "DR_Sqlite":
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}

func technologyForDriverName(driverName string) string {
switch driverName {
case "mysql":
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "postgres":
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "sqlite3":
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}
17 changes: 17 additions & 0 deletions pkg/report/frameworks/django/django.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,20 @@ type Database struct {
Name string `json:"name" yaml:"name"`
Engine string `json:"engine" yaml:"engine"`
}

func (value Database) GetTechnologyKey() string {
switch value.Engine {
case "sql_server.pyodbc":
return "e4db4505-b837-4b76-9184-c3cec3b5e522"
case "django.db.backends.mysql":
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "django.db.backends.oracle":
return "80886e2a-ee2c-423d-98bc-0a3d743787b4"
case "django.db.backends.postgresql":
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "django.db.backends.sqlite3":
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}
17 changes: 17 additions & 0 deletions pkg/report/frameworks/dotnet/dotnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,20 @@ type DBContext struct {
UseDbMethodName string `json:"use_db_method_name" yaml:"use_db_method_name"`
TypeName string `json:"type_name" yaml:"type_name"`
}

func (value DBContext) GetTechnologyKey() string {
switch value.UseDbMethodName {
case "UseSqlServer":
return "e4db4505-b837-4b76-9184-c3cec3b5e522"
case "UseMySQL":
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "UseOracle":
return "80886e2a-ee2c-423d-98bc-0a3d743787b4"
case "UseNpgsql":
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "UseSqlite":
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}
26 changes: 13 additions & 13 deletions pkg/report/frameworks/rails/rails.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,36 +25,36 @@ type Storage struct {
Encryption string `json:"encryption" yaml:"encryption"`
}

func (value *Cache) GetTechnologyKey() string {
func (value Cache) GetTechnologyKey() string {
switch value.Type {
case "memory_store", "null_store":
// Ignored cache types
return ""
case "file_store":
return "Disk"
return "39747024-c306-4a95-a0df-7e585a33a86f"
case "mem_cache_store":
return "Memcached"
return "42908ccc-4f0f-419e-9ba0-f25121fd15b7"
case "redis_cache_store":
return "Redis"
return "62c20409-c1bf-4be9-a859-6fe6be7b11e3"
default:
return "unidentified_data_store"
}
}

func (value *Database) GetTechnologyKey() string {
func (value Database) GetTechnologyKey() string {
switch strings.ToLower(value.Adapter) {
case "mysql2", "jdbcmysql":
return "MySQL"
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "postgresql", "jdbcpostgresql":
return "PostgreSQL"
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "sqlite3", "jdbcsqlite3":
return "SQLite"
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}

func (value *Storage) GetTechnologyKey() string {
func (value Storage) GetTechnologyKey() string {
if strings.Contains(value.Name, "test") {
// Ignored storage types
return ""
Expand All @@ -65,13 +65,13 @@ func (value *Storage) GetTechnologyKey() string {
// Ignored storage types
return ""
case "AzureStorage":
return "Azure Storage"
return "f0f43ee7-7f6b-4572-aaa0-6b207146912b"
case "Disk":
return "Disk"
return "39747024-c306-4a95-a0df-7e585a33a86f"
case "GCS":
return "Google Cloud Storage"
return "3a154582-174f-4ef7-90a2-f654435c23cb"
case "S3":
return "AWS S3"
return "4e5a3a3a-47cd-4b0e-b0a6-fa30a0a62499"
default:
return "unidentified_data_store"
}
Expand Down
23 changes: 22 additions & 1 deletion pkg/report/frameworks/spring/spring.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
package spring

import "github.com/bearer/curio/pkg/report/frameworks"
import (
"github.com/bearer/curio/pkg/report/frameworks"
)

const TypeDatabase frameworks.Type = "database"

type DataStore struct {
Driver string `json:"driver" yaml:"driver"`
}

func (value DataStore) GetTechnologyKey() string {
switch value.Driver {
case "db2", "com.ibm.db2.jcc.DB2Driver":
return "b9bbbbb8-cb8b-4ffb-997f-e0d1e9050a96"
case "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver":
return "e4db4505-b837-4b76-9184-c3cec3b5e522"
case "mysql", "com.mysql.jdbc.Driver", "mariadb", "org.mariadb.jdbc.Driver":
return "ffa70264-2b19-445d-a5c9-be82b64fe750"
case "oracle", "oracle.jdbc.OracleDriver":
return "80886e2a-ee2c-423d-98bc-0a3d743787b4"
case "postgresql", "com.postgresql.jdbc.Driver":
return "428ff7dd-22ea-4e80-8755-84c70cf460db"
case "sqlite", "org.sqlite.JDBC":
return "aa706b3c-0f6d-4a7b-a7a5-71ee0c5b6c00"
default:
return "unidentified_data_store"
}
}
Loading

0 comments on commit f7fc96a

Please sign in to comment.