Skip to content

Commit 7eeb37f

Browse files
authored
Merge pull request #114 from Threagile/editing-ui-poc
Initial implementation for front end for editing model via UI
2 parents 10f262a + 22c6cdf commit 7eeb37f

14 files changed

+1478
-225
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ repos:
1313
- id: go-imports
1414
- id: no-go-testing
1515
- id: golangci-lint
16-
- id: go-unit-tests
16+
- id: go-unit-tests

docs/mode-server.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,24 @@ The server is using [gin](https://github.com/gin-gonic/gin) to serve HTTP connec
77

88
- do not support [includes](./includes.md)
99
- single threaded - because of dependency on running graphviz as a process
10+
11+
## Edit feature
12+
13+
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.
14+
But most viable product here would be adding an ability to do changes to your model via UI.
15+
16+
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.
17+
However it's giving an idea of how feature is going to be used.
18+
19+
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.
20+
21+
The UI is fully based on the [schema.json](../support/schema.json).
22+
23+
There are a lot of improvements for the feature such as:
24+
25+
1. Allow adding custom risk tracking (currently it's broken).
26+
2. Improve changing technical assets (remember all of dragging around).
27+
3. Add question mark with explanation for each field.
28+
4. Allow to resize technical asset rectangle.
29+
5. Propagate id changes (for example if technical asset id changed it needs to be changed in risk tracking as well).
30+
6. Model validation.

pkg/model/read.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ func ReadAndAnalyzeModel(config configReader, builtinRiskRules types.RiskRules,
8383
return nil, fmt.Errorf("unable to load model yaml: %w", loadError)
8484
}
8585

86+
return AnalyzeModel(modelInput, config, builtinRiskRules, customRiskRules, progressReporter)
87+
}
88+
89+
func AnalyzeModel(modelInput *input.Model, config configReader, builtinRiskRules types.RiskRules, customRiskRules types.RiskRules, progressReporter types.ProgressReporter) (*ReadResult, error) {
90+
8691
parsedModel, parseError := ParseModel(config, modelInput, builtinRiskRules, customRiskRules)
8792
if parseError != nil {
8893
return nil, fmt.Errorf("unable to parse model yaml: %w", parseError)

pkg/risks/builtin/container_platform_escape_rule_test.go

Lines changed: 32 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ import (
77
"github.com/threagile/threagile/pkg/types"
88
)
99

10-
func TestCrossSiteRequestForgeryRuleGenerateRisksEmptyModelNotRisksCreated(t *testing.T) {
11-
rule := NewCrossSiteRequestForgeryRule()
10+
func TestContainerPlatformEscapeRuleGenerateRisksEmptyModelNotRisksCreated(t *testing.T) {
11+
rule := NewContainerPlatformEscapeRule()
1212

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

1515
assert.Nil(t, err)
1616
assert.Empty(t, risks)
1717
}
1818

19-
func TestCrossSiteRequestForgeryRuleGenerateRisksOutOfScopeNotRisksCreated(t *testing.T) {
20-
rule := NewCrossSiteRequestForgeryRule()
19+
func TestContainerPlatformEscapeRuleGenerateRisksOutOfScopeNotRisksCreated(t *testing.T) {
20+
rule := NewContainerPlatformEscapeRule()
2121

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

34-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRisksCreated(t *testing.T) {
35-
rule := NewCrossSiteRequestForgeryRule()
34+
func TestContainerPlatformEscapeRuleRuleGenerateRisksTechAssetNotContainerPlatformNotRisksCreated(t *testing.T) {
35+
rule := NewContainerPlatformEscapeRule()
3636

3737
risks, err := rule.GenerateRisks(&types.Model{
3838
TechnicalAssets: map[string]*types.TechnicalAsset{
@@ -41,7 +41,7 @@ func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRi
4141
{
4242
Name: "tool",
4343
Attributes: map[string]bool{
44-
types.WebApplication: false,
44+
types.ContainerPlatform: false,
4545
},
4646
},
4747
},
@@ -53,208 +53,66 @@ func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetNotWebApplicationNotRi
5353
assert.Empty(t, risks)
5454
}
5555

56-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationWithoutIncomingCommunicationNotRisksCreated(t *testing.T) {
57-
rule := NewCrossSiteRequestForgeryRule()
56+
func TestContainerPlatformEscapeRuleGenerateRisksTechAssetContainerPlatformRisksCreated(t *testing.T) {
57+
rule := NewContainerPlatformEscapeRule()
5858

5959
risks, err := rule.GenerateRisks(&types.Model{
6060
TechnicalAssets: map[string]*types.TechnicalAsset{
6161
"ta1": {
62-
Technologies: types.TechnologyList{
63-
{
64-
Name: "web-app",
65-
Attributes: map[string]bool{
66-
types.WebApplication: true,
67-
},
68-
},
69-
},
70-
},
71-
},
72-
})
73-
74-
assert.Nil(t, err)
75-
assert.Empty(t, risks)
76-
}
77-
78-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestNotWebAccessProtocolNotRiskCreated(t *testing.T) {
79-
rule := NewCrossSiteRequestForgeryRule()
80-
81-
risks, err := rule.GenerateRisks(&types.Model{
82-
TechnicalAssets: map[string]*types.TechnicalAsset{
83-
"web-app": {
84-
Id: "web-app",
85-
Technologies: types.TechnologyList{
86-
{
87-
Name: "web-app",
88-
Attributes: map[string]bool{
89-
types.WebApplication: true,
90-
},
91-
},
92-
},
93-
},
94-
"file-scrapper": {
62+
Id: "ta1",
63+
Title: "Docker",
9564
Technologies: types.TechnologyList{
9665
{
9766
Name: "tool",
98-
},
99-
},
100-
},
101-
},
102-
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
103-
"web-app": {
104-
{
105-
Protocol: types.LocalFileAccess,
106-
SourceId: "file-scrapper",
107-
TargetId: "web-app",
108-
},
109-
},
110-
},
111-
})
112-
113-
assert.Nil(t, err)
114-
assert.Empty(t, risks)
115-
}
116-
117-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolRiskCreated(t *testing.T) {
118-
rule := NewCrossSiteRequestForgeryRule()
119-
120-
risks, err := rule.GenerateRisks(&types.Model{
121-
TechnicalAssets: map[string]*types.TechnicalAsset{
122-
"web-app": {
123-
Id: "web-app",
124-
Title: "Web Application",
125-
Technologies: types.TechnologyList{
126-
{
127-
Name: "web-app",
128-
Attributes: map[string]bool{
129-
types.WebApplication: true,
130-
},
131-
},
132-
},
133-
},
134-
"user": {
135-
Title: "user",
136-
Technologies: types.TechnologyList{
137-
{
138-
Name: "user",
139-
},
140-
},
141-
},
142-
},
143-
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
144-
"web-app": {
145-
{
146-
Title: "HTTP",
147-
Protocol: types.HTTP,
148-
SourceId: "user",
149-
TargetId: "web-app",
150-
},
151-
},
152-
},
153-
})
154-
155-
assert.Nil(t, err)
156-
assert.NotEmpty(t, risks)
157-
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)
158-
assert.Equal(t, types.VeryLikely, risks[0].ExploitationLikelihood)
159-
assert.Equal(t, types.LowImpact, risks[0].ExploitationImpact)
160-
}
161-
162-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolViaDevOpsRiskCreatedWithLikelyLikelihood(t *testing.T) {
163-
rule := NewCrossSiteRequestForgeryRule()
164-
165-
risks, err := rule.GenerateRisks(&types.Model{
166-
TechnicalAssets: map[string]*types.TechnicalAsset{
167-
"web-app": {
168-
Id: "web-app",
169-
Title: "Web Application",
170-
Technologies: types.TechnologyList{
171-
{
172-
Name: "web-app",
17367
Attributes: map[string]bool{
174-
types.WebApplication: true,
68+
types.ContainerPlatform: true,
17569
},
17670
},
17771
},
178-
},
179-
"ci/cd": {
180-
Title: "ci/cd",
181-
Technologies: types.TechnologyList{
182-
{
183-
Name: "ci/cd",
184-
},
185-
},
186-
},
187-
},
188-
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
189-
"web-app": {
190-
{
191-
Title: "HTTP",
192-
Protocol: types.HTTP,
193-
SourceId: "ci/cd",
194-
TargetId: "web-app",
195-
Usage: types.DevOps,
196-
},
72+
Machine: types.Container,
19773
},
19874
},
19975
})
20076

20177
assert.Nil(t, err)
20278
assert.NotEmpty(t, risks)
203-
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)
204-
assert.Equal(t, types.Likely, risks[0].ExploitationLikelihood)
205-
assert.Equal(t, types.LowImpact, risks[0].ExploitationImpact)
79+
assert.Equal(t, "<b>Container Platform Escape</b> risk at <b>Docker</b>", risks[0].Title)
80+
assert.Equal(t, types.MediumImpact, risks[0].ExploitationImpact)
81+
assert.NotEmpty(t, risks[0].DataBreachTechnicalAssetIDs)
82+
assert.Equal(t, "ta1", risks[0].DataBreachTechnicalAssetIDs[0])
20683
}
20784

208-
func TestCrossSiteRequestForgeryRuleGenerateRisksTechAssetWebApplicationIncomingRequestWebAccessProtocolRiskCreatedWithMediumImpactWhenIntegrityIsMissionCritical(t *testing.T) {
209-
rule := NewCrossSiteRequestForgeryRule()
85+
func TestContainerPlatformEscapeRuleGenerateRisksTechAssetProcessStrictlyConfidentialDataAssetHighImpactRiskCreated(t *testing.T) {
86+
rule := NewContainerPlatformEscapeRule()
21087

21188
risks, err := rule.GenerateRisks(&types.Model{
21289
TechnicalAssets: map[string]*types.TechnicalAsset{
213-
"web-app": {
214-
Id: "web-app",
215-
Title: "Web Application",
90+
"ta1": {
91+
Id: "ta1",
92+
Title: "Docker",
21693
Technologies: types.TechnologyList{
21794
{
218-
Name: "web-app",
95+
Name: "tool",
21996
Attributes: map[string]bool{
220-
types.WebApplication: true,
97+
types.ContainerPlatform: true,
22198
},
22299
},
223100
},
224-
},
225-
"user": {
226-
Title: "user",
227-
Technologies: types.TechnologyList{
228-
{
229-
Name: "user",
230-
},
231-
},
101+
Machine: types.Container,
102+
DataAssetsProcessed: []string{"strictly-confidential-data-asset"},
232103
},
233104
},
234105
DataAssets: map[string]*types.DataAsset{
235-
"mission-critical-data": {
236-
Id: "mission-critical-data",
237-
Title: "Mission Critical Data",
238-
Integrity: types.MissionCritical,
239-
},
240-
},
241-
242-
IncomingTechnicalCommunicationLinksMappedByTargetId: map[string][]*types.CommunicationLink{
243-
"web-app": {
244-
{
245-
Title: "HTTP",
246-
Protocol: types.HTTP,
247-
SourceId: "user",
248-
TargetId: "web-app",
249-
DataAssetsReceived: []string{"mission-critical-data"},
250-
},
106+
"strictly-confidential-data-asset": {
107+
Confidentiality: types.StrictlyConfidential,
251108
},
252109
},
253110
})
254111

255112
assert.Nil(t, err)
256113
assert.NotEmpty(t, risks)
257-
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)
258-
assert.Equal(t, types.VeryLikely, risks[0].ExploitationLikelihood)
259-
assert.Equal(t, types.MediumImpact, risks[0].ExploitationImpact)
114+
assert.Equal(t, "<b>Container Platform Escape</b> risk at <b>Docker</b>", risks[0].Title)
115+
assert.Equal(t, types.HighImpact, risks[0].ExploitationImpact)
116+
assert.NotEmpty(t, risks[0].DataBreachTechnicalAssetIDs)
117+
assert.Equal(t, "ta1", risks[0].DataBreachTechnicalAssetIDs[0])
260118
}

0 commit comments

Comments
 (0)