Skip to content

Commit

Permalink
Merge pull request #114 from Threagile/editing-ui-poc
Browse files Browse the repository at this point in the history
Initial implementation for front end for editing model via UI
  • Loading branch information
ezavgorodniy authored Nov 25, 2024
2 parents 10f262a + 22c6cdf commit 7eeb37f
Show file tree
Hide file tree
Showing 14 changed files with 1,478 additions and 225 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ repos:
- id: go-imports
- id: no-go-testing
- id: golangci-lint
- id: go-unit-tests
- id: go-unit-tests
21 changes: 21 additions & 0 deletions docs/mode-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,24 @@ The server is using [gin](https://github.com/gin-gonic/gin) to serve HTTP connec

- do not support [includes](./includes.md)
- single threaded - because of dependency on running graphviz as a process

## Edit feature

In server mode you can also go and edit model, run analysis on it in UI. The feature is under development and that's only very first iteration is ready.
But most viable product here would be adding an ability to do changes to your model via UI.

You can find a demo [here](https://www.youtube.com/watch?v=G9nwg-nqOCw). Feature is still under development and video may be a bit obsolete.
However it's giving an idea of how feature is going to be used.

Most important note that the model is fully in the memory of the browser and to store it somewhere you need to export it to your hard drive.

The UI is fully based on the [schema.json](../support/schema.json).

There are a lot of improvements for the feature such as:

1. Allow adding custom risk tracking (currently it's broken).
2. Improve changing technical assets (remember all of dragging around).
3. Add question mark with explanation for each field.
4. Allow to resize technical asset rectangle.
5. Propagate id changes (for example if technical asset id changed it needs to be changed in risk tracking as well).
6. Model validation.
5 changes: 5 additions & 0 deletions pkg/model/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ func ReadAndAnalyzeModel(config configReader, builtinRiskRules types.RiskRules,
return nil, fmt.Errorf("unable to load model yaml: %w", loadError)
}

return AnalyzeModel(modelInput, config, builtinRiskRules, customRiskRules, progressReporter)
}

func AnalyzeModel(modelInput *input.Model, config configReader, builtinRiskRules types.RiskRules, customRiskRules types.RiskRules, progressReporter types.ProgressReporter) (*ReadResult, error) {

parsedModel, parseError := ParseModel(config, modelInput, builtinRiskRules, customRiskRules)
if parseError != nil {
return nil, fmt.Errorf("unable to parse model yaml: %w", parseError)
Expand Down
206 changes: 32 additions & 174 deletions pkg/risks/builtin/container_platform_escape_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import (
"github.com/threagile/threagile/pkg/types"
)

func TestCrossSiteRequestForgeryRuleGenerateRisksEmptyModelNotRisksCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()
func TestContainerPlatformEscapeRuleGenerateRisksEmptyModelNotRisksCreated(t *testing.T) {
rule := NewContainerPlatformEscapeRule()

risks, err := rule.GenerateRisks(&types.Model{})

assert.Nil(t, err)
assert.Empty(t, risks)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksOutOfScopeNotRisksCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()
func TestContainerPlatformEscapeRuleGenerateRisksOutOfScopeNotRisksCreated(t *testing.T) {
rule := NewContainerPlatformEscapeRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
Expand All @@ -31,8 +31,8 @@ func TestCrossSiteRequestForgeryRuleGenerateRisksOutOfScopeNotRisksCreated(t *te
assert.Empty(t, risks)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRisksCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()
func TestContainerPlatformEscapeRuleRuleGenerateRisksTechAssetNotContainerPlatformNotRisksCreated(t *testing.T) {
rule := NewContainerPlatformEscapeRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
Expand All @@ -41,7 +41,7 @@ func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRi
{
Name: "tool",
Attributes: map[string]bool{
types.WebApplication: false,
types.ContainerPlatform: false,
},
},
},
Expand All @@ -53,208 +53,66 @@ func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRi
assert.Empty(t, risks)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationWithoutIncomingCommunicationNotRisksCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()
func TestContainerPlatformEscapeRuleGenerateRisksTechAssetContainerPlatformRisksCreated(t *testing.T) {
rule := NewContainerPlatformEscapeRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
"ta1": {
Technologies: types.TechnologyList{
{
Name: "web-app",
Attributes: map[string]bool{
types.WebApplication: true,
},
},
},
},
},
})

assert.Nil(t, err)
assert.Empty(t, risks)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestNotWebAccessProtocolNotRiskCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
"web-app": {
Id: "web-app",
Technologies: types.TechnologyList{
{
Name: "web-app",
Attributes: map[string]bool{
types.WebApplication: true,
},
},
},
},
"file-scrapper": {
Id: "ta1",
Title: "Docker",
Technologies: types.TechnologyList{
{
Name: "tool",
},
},
},
},
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
"web-app": {
{
Protocol: types.LocalFileAccess,
SourceId: "file-scrapper",
TargetId: "web-app",
},
},
},
})

assert.Nil(t, err)
assert.Empty(t, risks)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolRiskCreated(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
"web-app": {
Id: "web-app",
Title: "Web Application",
Technologies: types.TechnologyList{
{
Name: "web-app",
Attributes: map[string]bool{
types.WebApplication: true,
},
},
},
},
"user": {
Title: "user",
Technologies: types.TechnologyList{
{
Name: "user",
},
},
},
},
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
"web-app": {
{
Title: "HTTP",
Protocol: types.HTTP,
SourceId: "user",
TargetId: "web-app",
},
},
},
})

assert.Nil(t, err)
assert.NotEmpty(t, risks)
assert.Equal(t, "<b>Cross-Site Request Forgery (CSRF)</b> risk at <b>Web Application</b> via <b>HTTP</b> from <b>user</b>", risks[0].Title)
assert.Equal(t, types.VeryLikely, risks[0].ExploitationLikelihood)
assert.Equal(t, types.LowImpact, risks[0].ExploitationImpact)
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolViaDevOpsRiskCreatedWithLikelyLikelihood(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
"web-app": {
Id: "web-app",
Title: "Web Application",
Technologies: types.TechnologyList{
{
Name: "web-app",
Attributes: map[string]bool{
types.WebApplication: true,
types.ContainerPlatform: true,
},
},
},
},
"ci/cd": {
Title: "ci/cd",
Technologies: types.TechnologyList{
{
Name: "ci/cd",
},
},
},
},
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
"web-app": {
{
Title: "HTTP",
Protocol: types.HTTP,
SourceId: "ci/cd",
TargetId: "web-app",
Usage: types.DevOps,
},
Machine: types.Container,
},
},
})

assert.Nil(t, err)
assert.NotEmpty(t, risks)
assert.Equal(t, "<b>Cross-Site Request Forgery (CSRF)</b> risk at <b>Web Application</b> via <b>HTTP</b> from <b>ci/cd</b>", risks[0].Title)
assert.Equal(t, types.Likely, risks[0].ExploitationLikelihood)
assert.Equal(t, types.LowImpact, risks[0].ExploitationImpact)
assert.Equal(t, "<b>Container Platform Escape</b> risk at <b>Docker</b>", risks[0].Title)
assert.Equal(t, types.MediumImpact, risks[0].ExploitationImpact)
assert.NotEmpty(t, risks[0].DataBreachTechnicalAssetIDs)
assert.Equal(t, "ta1", risks[0].DataBreachTechnicalAssetIDs[0])
}

func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolRiskCreatedWithMediumImpactWhenIntegrityIsMissionCritical(t *testing.T) {
rule := NewCrossSiteRequestForgeryRule()
func TestContainerPlatformEscapeRuleGenerateRisksTechAssetProcessStrictlyConfidentialDataAssetHighImpactRiskCreated(t *testing.T) {
rule := NewContainerPlatformEscapeRule()

risks, err := rule.GenerateRisks(&types.Model{
TechnicalAssets: map[string]*types.TechnicalAsset{
"web-app": {
Id: "web-app",
Title: "Web Application",
"ta1": {
Id: "ta1",
Title: "Docker",
Technologies: types.TechnologyList{
{
Name: "web-app",
Name: "tool",
Attributes: map[string]bool{
types.WebApplication: true,
types.ContainerPlatform: true,
},
},
},
},
"user": {
Title: "user",
Technologies: types.TechnologyList{
{
Name: "user",
},
},
Machine: types.Container,
DataAssetsProcessed: []string{"strictly-confidential-data-asset"},
},
},
DataAssets: map[string]*types.DataAsset{
"mission-critical-data": {
Id: "mission-critical-data",
Title: "Mission Critical Data",
Integrity: types.MissionCritical,
},
},

IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
"web-app": {
{
Title: "HTTP",
Protocol: types.HTTP,
SourceId: "user",
TargetId: "web-app",
DataAssetsReceived: []string{"mission-critical-data"},
},
"strictly-confidential-data-asset": {
Confidentiality: types.StrictlyConfidential,
},
},
})

assert.Nil(t, err)
assert.NotEmpty(t, risks)
assert.Equal(t, "<b>Cross-Site Request Forgery (CSRF)</b> risk at <b>Web Application</b> via <b>HTTP</b> from <b>user</b>", risks[0].Title)
assert.Equal(t, types.VeryLikely, risks[0].ExploitationLikelihood)
assert.Equal(t, types.MediumImpact, risks[0].ExploitationImpact)
assert.Equal(t, "<b>Container Platform Escape</b> risk at <b>Docker</b>", risks[0].Title)
assert.Equal(t, types.HighImpact, risks[0].ExploitationImpact)
assert.NotEmpty(t, risks[0].DataBreachTechnicalAssetIDs)
assert.Equal(t, "ta1", risks[0].DataBreachTechnicalAssetIDs[0])
}
Loading

0 comments on commit 7eeb37f

Please sign in to comment.