Skip to content

Commit 8599ed9

Browse files
Merged in release/1.3.0 (pull request #53)
Release/1.3.0
2 parents bd3e3af + 3672dd4 commit 8599ed9

File tree

106 files changed

+4526
-975
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+4526
-975
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,7 @@ caret
118118
**/threatmodel.otm
119119
startleft/*.otm
120120
customerexamples
121+
/tmp/
121122
/.run/
123+
/bin/
124+
/startleft/bin/

examples/manual/manual.otm

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,48 @@ project:
44
name: Manual ThreatModel
55
id: manual-threatmodel
66

7-
trustzones:
8-
- id: internet
7+
trustZones:
8+
- id: f0ba7722-39b6-4c81-8290-a30a248bb8d9
99
name: Internet
10-
type: internet
10+
risk:
11+
trustRating: 1
1112

12-
- id: web
13-
name: Web
14-
type: private
13+
- id: 6376d53e-6461-412b-8e04-7b3fe2b397de
14+
name: Public
15+
risk:
16+
trustRating: 1
1517

16-
- id: data
17-
name: Data
18-
type: private
18+
- id: 2ab4effa-40b7-4cd2-ba81-8247d29a6f2d
19+
name: Private Secured
20+
risk:
21+
trustRating: 100
1922

2023
components:
2124
- id: user
2225
name: User
23-
type: user
24-
parent: internet
26+
type: generic-client
27+
parent:
28+
trustZone: f0ba7722-39b6-4c81-8290-a30a248bb8d9
2529

2630
- id: web-server
2731
name: Web server
28-
type: webapp
29-
parent: web
32+
type: web-application-server-side
33+
parent:
34+
trustZone: 6376d53e-6461-412b-8e04-7b3fe2b397de
3035

3136
- id: database
3237
name: Database
33-
type: postgres
34-
parent: data
38+
type: postgresql
39+
parent:
40+
trustZone: 2ab4effa-40b7-4cd2-ba81-8247d29a6f2d
3541

3642
dataflows:
3743
- id: client-connection
3844
name: Client connection
39-
type: network
40-
from: user
41-
to: web-server
45+
source: user
46+
destination: web-server
4247

4348
- id: database-connection
4449
name: Database connection
45-
type: network
46-
from: web-server
47-
to: database
48-
49-
50+
source: web-server
51+
destination: database
45.9 KB
Binary file not shown.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
trustzones:
2+
- label: Public Cloud
3+
type: Public Cloud
4+
id: b61d6911-338d-46a8-9f39-8dcd24abfe91
5+
6+
- label: Private Secured Cloud
7+
type: Private Secured
8+
id: 2ab4effa-40b7-4cd2-ba81-8247d29a6f2d
9+
10+
components:
11+
- label: Amazon EC2
12+
type: ec2
13+
14+
- label: Amazon CloudWatch
15+
type: cloudwatch
16+
17+
- label: Database
18+
type: rds
19+
20+
- label: Amazon Athena
21+
type: athena
22+
23+
- label: Amazon MQ
24+
type: CD-MQ
25+
26+
- label: Amazon API Gateway Endpoint
27+
type: api-gateway
28+
29+
- label: Amazon CloudFront
30+
type: cf-cloudfront
31+
32+
- label: Bucket
33+
type: s3
34+
35+
dataflows: []

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
'fastapi',
2525
'python-multipart',
2626
'click',
27-
'uvicorn'
27+
'uvicorn',
28+
'shapely',
29+
'vsdx',
30+
'python-magic'
2831
],
2932
use_scm_version=True,
3033
extras_require={
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import logging
2+
3+
from fastapi import APIRouter, File, UploadFile, Form, Response
4+
5+
from startleft.api.controllers.otm_controller import RESPONSE_STATUS_CODE, PREFIX, controller_responses
6+
from startleft.diagram.diagram_type import DiagramType
7+
from startleft.otm.otm_project import OtmProject
8+
9+
URL = '/diagram'
10+
11+
logger = logging.getLogger(__name__)
12+
13+
router = APIRouter(
14+
prefix=PREFIX,
15+
responses=controller_responses
16+
)
17+
18+
19+
@router.post(URL, status_code=RESPONSE_STATUS_CODE, description="Generates an OTM threat model from an Diagram file",
20+
tags=['Diagram'])
21+
def diagram(diag_file: UploadFile = File(..., description="File that contains the Iac definition"),
22+
diag_type: DiagramType = Form(..., description="Type of Diagram File: VISIO"),
23+
id: str = Form(..., description="ID of the new project"),
24+
name: str = Form(..., description="Name of the new project"),
25+
default_mapping_file: UploadFile = File(..., description="File that contains the default mapping file"),
26+
custom_mapping_file: UploadFile = File(None, description="File that contains the user custom mapping file")):
27+
logger.info(
28+
f"POST request received for creating new project with id {id} and name {name} from Diagram {diag_type} file")
29+
30+
logger.info("Parsing Diagram file to OTM")
31+
32+
mapping_data_list = []
33+
34+
with default_mapping_file.file as f:
35+
mapping_data_list.append(f.read())
36+
37+
if custom_mapping_file:
38+
with custom_mapping_file.file as f:
39+
mapping_data_list.append(f.read())
40+
41+
otm_project = OtmProject.from_diag_file_to_otm_stream(id, name, diag_type, [diag_file.file], mapping_data_list)
42+
43+
return Response(status_code=201, media_type="application/json", content=otm_project.get_otm_as_json())

startleft/api/controllers/health/health_controller.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from fastapi import APIRouter
22
from fastapi.responses import JSONResponse
33

4+
from startleft.version import version
5+
46
PREFIX = ''
57
URL = '/health'
6-
RESPONSE_BODY_STARTLEFT_OK = {"status": "OK", "components": {"StartLeft": "OK"}}
8+
RESPONSE_BODY_STARTLEFT_OK = {"status": "OK", "version": version, "components": {"StartLeft": "OK"}}
79

810
router = APIRouter(prefix=PREFIX)
911

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,19 @@
11
import logging
22

3-
from http import HTTPStatus
4-
53
from fastapi import APIRouter, File, UploadFile, Form, Response
64

7-
from startleft.api.controllers.iac.iac_type import IacType
8-
from startleft.api.error_response import ErrorResponse
9-
from startleft.messages import messages
10-
from startleft.project.otm_project import OtmProject
5+
from startleft.api.controllers.otm_controller import RESPONSE_STATUS_CODE, PREFIX, controller_responses
6+
from startleft.iac.iac_type import IacType
7+
from startleft.otm.otm_project import OtmProject
118

12-
PREFIX = '/api/v1/startleft/iac'
13-
URL = ''
14-
RESPONSE_STATUS_CODE = HTTPStatus.CREATED
9+
URL = '/iac'
1510

1611
logger = logging.getLogger(__name__)
1712

1813
router = APIRouter(
1914
prefix=PREFIX,
2015
tags=["IaC"],
21-
responses={
22-
201: {"description": messages.OTM_SUCCESSFULLY_CREATED},
23-
400: {"description": messages.BAD_REQUEST,
24-
"model": ErrorResponse},
25-
401: {"description": messages.UNAUTHORIZED_EXCEPTION,
26-
"model": ErrorResponse},
27-
403: {"description": messages.FORBIDDEN_OPERATION,
28-
"model": ErrorResponse},
29-
'default': {"description": messages.UNEXPECTED_API_ERROR,
30-
"model": ErrorResponse}
31-
}
16+
responses=controller_responses
3217
)
3318

3419

@@ -42,8 +27,13 @@ def iac(iac_file: UploadFile = File(..., description="File that contains the Iac
4227
logger.info(f"POST request received for creating new project with id {id} and name {name} from IaC {iac_type} file")
4328

4429
logger.info("Parsing IaC file to OTM")
45-
otm_project = OtmProject.from_iac_file_to_otm_stream(id, name, iac_type, [iac_file.file]
46-
, [mapping_file.file] if mapping_file else [])
30+
with iac_file.file as f:
31+
iac_data = f.read()
4732

48-
return Response(status_code=201, media_type="application/json", content=otm_project.get_otm_as_json())
33+
with mapping_file.file as f:
34+
mapping_data = f.read()
4935

36+
otm_project = OtmProject.from_iac_file_to_otm_stream(id, name, iac_type, [iac_data],
37+
[mapping_data] if mapping_file else [])
38+
39+
return Response(status_code=201, media_type="application/json", content=otm_project.get_otm_as_json())

startleft/api/controllers/iac/iac_type.py

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from http import HTTPStatus
2+
3+
from startleft import messages
4+
from startleft.api.error_response import ErrorResponse
5+
6+
controller_responses = {
7+
201: {"description": messages.OTM_SUCCESSFULLY_CREATED},
8+
400: {"description": messages.BAD_REQUEST,
9+
"model": ErrorResponse},
10+
401: {"description": messages.UNAUTHORIZED_EXCEPTION,
11+
"model": ErrorResponse},
12+
403: {"description": messages.FORBIDDEN_OPERATION,
13+
"model": ErrorResponse},
14+
'default': {"description": messages.UNEXPECTED_API_ERROR,
15+
"model": ErrorResponse}
16+
}
17+
18+
PREFIX = '/api/v1/startleft'
19+
20+
RESPONSE_STATUS_CODE = HTTPStatus.CREATED

startleft/api/errors.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from enum import Enum
22

3-
from startleft.messages import messages
3+
from startleft import messages
44

55

66
class ErrorCode(Enum):
@@ -9,6 +9,7 @@ class ErrorCode(Enum):
99
OTM_TO_IR_EXIT_UNEXPECTED = (3, "OtmToIrUnexpectedError")
1010
OTM_TO_IR_EXIT_VALIDATION_FAILED = (4, "OtmToIrValidationError")
1111
MAPPING_FILE_EXIT_VALIDATION_FAILED = (5, "MalformedMappingFile")
12+
DIAGRAM_TO_OTM_EXIT_VALIDATION_FAILED = (6, "DiagramToOtmValidationError")
1213

1314
def __init__(self, system_exit_status, error_type):
1415
self.system_exit_status = system_exit_status
@@ -61,3 +62,36 @@ class WriteThreatModelError(CommonError):
6162
message = messages.ERROR_WRITING_THREAT_MODEL
6263
http_status_code = 500
6364
error_code = ErrorCode.IAC_TO_OTM_EXIT_UNEXPECTED
65+
66+
67+
class UnknownDiagramType(CommonError):
68+
message = messages.CANNOT_RECOGNIZE_GIVEN_DIAGRAM_TYPE
69+
http_status_code = 400
70+
system_exit_status = ErrorCode.IAC_TO_OTM_EXIT_UNEXPECTED
71+
72+
73+
class IacFileNotValidError(CommonError):
74+
message = messages.IAC_FILE_IS_NOT_VALID
75+
http_status_code = 400
76+
error_code = ErrorCode.IAC_TO_OTM_EXIT_VALIDATION_FAILED
77+
78+
def __init__(self, message: str):
79+
self.message = f"{self.message}. {message}"
80+
81+
82+
class DiagramFileNotValidError(CommonError):
83+
message = messages.DIAGRAM_FILE_IS_NOT_VALID
84+
http_status_code = 400
85+
error_code = ErrorCode.DIAGRAM_TO_OTM_EXIT_VALIDATION_FAILED
86+
87+
def __init__(self, message: str):
88+
self.message = f"{self.message}. {message}"
89+
90+
91+
class ParsingError(CommonError):
92+
message = messages.NOT_PARSEABLE_SOURCE_FILES
93+
http_status_code = 400
94+
95+
def __init__(self, message: str, error_code: ErrorCode):
96+
self.message = f"{self.message}. {message}"
97+
self.error_code = error_code

0 commit comments

Comments
 (0)