From c7e3a83cdb93e6a79652e3bf15e1ac2d1ef17850 Mon Sep 17 00:00:00 2001 From: Yevhen Zavhorodnii Date: Mon, 3 Jun 2024 11:17:10 +0100 Subject: [PATCH] Cover sql no sql injection rule with tests --- .../risks/builtin/sql_nosql_injection_rule.go | 7 +- .../builtin/sql_nosql_injection_rule_test.go | 155 ++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 pkg/security/risks/builtin/sql_nosql_injection_rule_test.go diff --git a/pkg/security/risks/builtin/sql_nosql_injection_rule.go b/pkg/security/risks/builtin/sql_nosql_injection_rule.go index 7e6fd08f..46331057 100644 --- a/pkg/security/risks/builtin/sql_nosql_injection_rule.go +++ b/pkg/security/risks/builtin/sql_nosql_injection_rule.go @@ -47,8 +47,11 @@ func (r *SqlNoSqlInjectionRule) GenerateRisks(input *types.Model) ([]*types.Risk if input.TechnicalAssets[incomingFlow.SourceId].OutOfScope { continue } - if incomingFlow.Protocol.IsPotentialDatabaseAccessProtocol(true) && technicalAsset.Technologies.GetAttribute(types.IsVulnerableToQueryInjection) || - incomingFlow.Protocol.IsPotentialDatabaseAccessProtocol(false) { + potentialDatabaseAccessProtocol := incomingFlow.Protocol.IsPotentialDatabaseAccessProtocol(true) + isVulnerableToQueryInjection := technicalAsset.Technologies.GetAttribute(types.IsVulnerableToQueryInjection) + potentialLaxDatabaseAccessProtocol := incomingFlow.Protocol.IsPotentialDatabaseAccessProtocol(false) + if potentialDatabaseAccessProtocol && isVulnerableToQueryInjection || + potentialLaxDatabaseAccessProtocol { risks = append(risks, r.createRisk(input, technicalAsset, incomingFlow)) } } diff --git a/pkg/security/risks/builtin/sql_nosql_injection_rule_test.go b/pkg/security/risks/builtin/sql_nosql_injection_rule_test.go new file mode 100644 index 00000000..e72f3459 --- /dev/null +++ b/pkg/security/risks/builtin/sql_nosql_injection_rule_test.go @@ -0,0 +1,155 @@ +package builtin + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/threagile/threagile/pkg/security/types" +) + +func TestSqlNoSqlInjectionRuleGenerateRisksEmptyModelNotRisksCreated(t *testing.T) { + rule := NewSqlNoSqlInjectionRule() + + risks, err := rule.GenerateRisks(&types.Model{}) + + assert.Nil(t, err) + assert.Empty(t, risks) +} + +func TestSqlNoSqlInjectionRuleGenerateRisksOutOfScopeNoRisksCreated(t *testing.T) { + rule := NewSqlNoSqlInjectionRule() + risks, err := rule.GenerateRisks(&types.Model{ + TechnicalAssets: map[string]*types.TechnicalAsset{ + "ta1": { + Title: "Test Technical Asset", + OutOfScope: true, + Technologies: types.TechnologyList{ + { + Name: "service-registry", + Attributes: map[string]bool{ + types.ServiceRegistry: true, + }, + }, + }, + }, + }, + }) + + assert.Nil(t, err) + assert.Empty(t, risks) +} + +type SqlNoSqlInjectionRuleTest struct { + confidentiality types.Confidentiality + integrity types.Criticality + usage types.Usage + + protocol types.Protocol + isVulnerableToQueryInjection bool + + expectRiskCreated bool + expectedLikelihood types.RiskExploitationLikelihood + expectedImpact types.RiskExploitationImpact +} + +func TestSqlNoSqlInjectionRuleCreateRisks(t *testing.T) { + testCases := map[string]SqlNoSqlInjectionRuleTest{ + "not database protocol": { + protocol: types.SmbEncrypted, + expectRiskCreated: false, + isVulnerableToQueryInjection: true, + }, + // TODO: understand + // "not vulnerable to query injection": { + // protocol: types.JdbcEncrypted, + // expectRiskCreated: false, + // isVulnerableToQueryInjection: false, + // }, + "database protocol and vulnerable to query injection": { + protocol: types.JdbcEncrypted, + expectRiskCreated: true, + isVulnerableToQueryInjection: true, + expectedLikelihood: types.VeryLikely, + expectedImpact: types.MediumImpact, + }, + "strictly confidential tech asset high impact": { + protocol: types.JdbcEncrypted, + expectRiskCreated: true, + isVulnerableToQueryInjection: true, + confidentiality: types.StrictlyConfidential, + integrity: types.Critical, + expectedLikelihood: types.VeryLikely, + expectedImpact: types.HighImpact, + }, + "mission critical integrity tech asset high impact": { + protocol: types.JdbcEncrypted, + expectRiskCreated: true, + isVulnerableToQueryInjection: true, + confidentiality: types.Confidential, + integrity: types.MissionCritical, + expectedLikelihood: types.VeryLikely, + expectedImpact: types.HighImpact, + }, + "devops usage likely likelihood": { + protocol: types.JdbcEncrypted, + expectRiskCreated: true, + isVulnerableToQueryInjection: true, + usage: types.DevOps, + confidentiality: types.Confidential, + integrity: types.Critical, + expectedLikelihood: types.Likely, + expectedImpact: types.MediumImpact, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + rule := NewSqlNoSqlInjectionRule() + risks, err := rule.GenerateRisks(&types.Model{ + TechnicalAssets: map[string]*types.TechnicalAsset{ + "ta1": { + Id: "ta1", + Title: "Test Technical Asset", + OutOfScope: false, + Technologies: types.TechnologyList{ + { + Name: "service-registry", + Attributes: map[string]bool{ + types.IsVulnerableToQueryInjection: testCase.isVulnerableToQueryInjection, + }, + }, + }, + Confidentiality: testCase.confidentiality, + Integrity: testCase.integrity, + }, + "ta2": { + Id: "ta2", + Title: "Caller Technical Asset", + OutOfScope: false, + }, + }, + IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{ + "ta1": { + { + Title: "Incoming Flow", + TargetId: "ta1", + SourceId: "ta2", + Protocol: testCase.protocol, + Usage: testCase.usage, + }, + }, + }, + }) + + assert.Nil(t, err) + if testCase.expectRiskCreated { + assert.Len(t, risks, 1) + assert.Equal(t, testCase.expectedImpact, risks[0].ExploitationImpact) + assert.Equal(t, testCase.expectedLikelihood, risks[0].ExploitationLikelihood) + assert.Equal(t, "SQL/NoSQL-Injection risk at Caller Technical Asset against database Test Technical Asset via Incoming Flow", risks[0].Title) + } else { + assert.Empty(t, risks) + } + }) + } +}