Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stored, sent and received data assets are always processed #18

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 54 additions & 19 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4088,23 +4088,25 @@ func parseModel(inputFilename string) {
panic(errors.New("unknown 'usage' value of technical asset '" + title + "': " + fmt.Sprintf("%v", asset.Usage)))
}

var dataAssetsProcessed = make([]string, 0)
if asset.Data_assets_processed != nil {
dataAssetsProcessed = make([]string, len(asset.Data_assets_processed))
for i, parsedProcessedAsset := range asset.Data_assets_processed {
referencedAsset := fmt.Sprintf("%v", parsedProcessedAsset)
checkDataAssetTargetExists(referencedAsset, "technical asset '"+title+"'")
dataAssetsProcessed[i] = referencedAsset
}
}

var dataAssetsStored = make([]string, 0)
if asset.Data_assets_stored != nil {
dataAssetsStored = make([]string, len(asset.Data_assets_stored))
for i, parsedStoredAssets := range asset.Data_assets_stored {
for _, parsedStoredAssets := range asset.Data_assets_stored {
referencedAsset := fmt.Sprintf("%v", parsedStoredAssets)
checkDataAssetTargetExists(referencedAsset, "technical asset '"+title+"'")
dataAssetsStored[i] = referencedAsset
if !model.Contains(dataAssetsStored, referencedAsset) {
checkDataAssetTargetExists(referencedAsset, "technical asset '"+title+"'")
dataAssetsStored = append(dataAssetsStored, referencedAsset)
}
}
}

var dataAssetsProcessed = dataAssetsStored
if asset.Data_assets_processed != nil {
for _, parsedProcessedAsset := range asset.Data_assets_processed {
referencedAsset := fmt.Sprintf("%v", parsedProcessedAsset)
if !model.Contains(dataAssetsProcessed, referencedAsset) {
checkDataAssetTargetExists(referencedAsset, "technical asset '"+title+"'")
dataAssetsProcessed = append(dataAssetsProcessed, referencedAsset)
}
}
}

Expand Down Expand Up @@ -4501,16 +4503,26 @@ func parseModel(inputFilename string) {
if commLink.Data_assets_sent != nil {
for _, dataAssetSent := range commLink.Data_assets_sent {
referencedAsset := fmt.Sprintf("%v", dataAssetSent)
checkDataAssetTargetExists(referencedAsset, "communication link '"+commLinkTitle+"' of technical asset '"+title+"'")
dataAssetsSent = append(dataAssetsSent, referencedAsset)
if !model.Contains(dataAssetsSent, referencedAsset) {
checkDataAssetTargetExists(referencedAsset, "communication link '"+commLinkTitle+"' of technical asset '"+title+"'")
dataAssetsSent = append(dataAssetsSent, referencedAsset)
if !model.Contains(dataAssetsProcessed, referencedAsset) {
dataAssetsProcessed = append(dataAssetsProcessed, referencedAsset)
}
}
}
}

if commLink.Data_assets_received != nil {
for _, dataAssetReceived := range commLink.Data_assets_received {
referencedAsset := fmt.Sprintf("%v", dataAssetReceived)
checkDataAssetTargetExists(referencedAsset, "communication link '"+commLinkTitle+"' of technical asset '"+title+"'")
dataAssetsReceived = append(dataAssetsReceived, referencedAsset)
if !model.Contains(dataAssetsSent, referencedAsset) {
checkDataAssetTargetExists(referencedAsset, "communication link '"+commLinkTitle+"' of technical asset '"+title+"'")
dataAssetsReceived = append(dataAssetsReceived, referencedAsset)
if !model.Contains(dataAssetsProcessed, referencedAsset) {
dataAssetsProcessed = append(dataAssetsProcessed, referencedAsset)
}
}
}
}

Expand Down Expand Up @@ -4586,6 +4598,29 @@ func parseModel(inputFilename string) {
}
}

// A target of a communication link implicitly processes all data assets that are sent to or received by that target
for id, techAsset := range model.ParsedModelRoot.TechnicalAssets {
for _, commLink := range techAsset.CommunicationLinks {
if commLink.TargetId == id {
continue
}
targetTechAsset := model.ParsedModelRoot.TechnicalAssets[commLink.TargetId]
dataAssetsProcessedByTarget := targetTechAsset.DataAssetsProcessed
for _, dataAssetSent := range commLink.DataAssetsSent {
if !model.Contains(dataAssetsProcessedByTarget, dataAssetSent) {
dataAssetsProcessedByTarget = append(dataAssetsProcessedByTarget, dataAssetSent)
}
}
for _, dataAssetReceived := range commLink.DataAssetsReceived {
if !model.Contains(dataAssetsProcessedByTarget, dataAssetReceived) {
dataAssetsProcessedByTarget = append(dataAssetsProcessedByTarget, dataAssetReceived)
}
}
targetTechAsset.DataAssetsProcessed = dataAssetsProcessedByTarget
model.ParsedModelRoot.TechnicalAssets[commLink.TargetId] = targetTechAsset
}
}

// Trust Boundaries ===============================================================================
checklistToAvoidAssetBeingModeledInMultipleTrustBoundaries := make(map[string]bool)
model.ParsedModelRoot.TrustBoundaries = make(map[string]model.TrustBoundary)
Expand Down Expand Up @@ -5137,7 +5172,7 @@ func writeDataAssetDiagramGraphvizDOT(diagramFilenameDOT string, dpi int) *os.Fi
}
sort.Sort(model.ByOrderAndIdSort(techAssets))
for _, technicalAsset := range techAssets {
if len(technicalAsset.DataAssetsStored) > 0 || len(technicalAsset.DataAssetsProcessed) > 0 {
if len(technicalAsset.DataAssetsProcessed) > 0 {
dotContent.WriteString(makeTechAssetNode(technicalAsset, true))
dotContent.WriteString("\n")
}
Expand Down
80 changes: 7 additions & 73 deletions model/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1146,9 +1146,6 @@ func (what DataAsset) IsDataBreachPotentialStillAtRisk() bool {
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsProcessed, what.Id) {
return true
}
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsStored, what.Id) {
return true
}
}
}
return false
Expand All @@ -1164,12 +1161,6 @@ func (what DataAsset) IdentifiedDataBreachProbability() DataBreachProbability {
break
}
}
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsStored, what.Id) {
if risk.DataBreachProbability > highestProbability {
highestProbability = risk.DataBreachProbability
break
}
}
}
}
return highestProbability
Expand All @@ -1185,12 +1176,6 @@ func (what DataAsset) IdentifiedDataBreachProbabilityStillAtRisk() DataBreachPro
break
}
}
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsStored, what.Id) {
if risk.DataBreachProbability > highestProbability {
highestProbability = risk.DataBreachProbability
break
}
}
}
}
return highestProbability
Expand All @@ -1204,10 +1189,6 @@ func (what DataAsset) IdentifiedDataBreachProbabilityRisksStillAtRisk() []Risk {
result = append(result, risk)
break
}
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsStored, what.Id) {
result = append(result, risk)
break
}
}
}
return result
Expand All @@ -1221,10 +1202,6 @@ func (what DataAsset) IdentifiedDataBreachProbabilityRisks() []Risk {
result = append(result, risk)
break
}
if Contains(ParsedModelRoot.TechnicalAssets[techAsset].DataAssetsStored, what.Id) {
result = append(result, risk)
break
}
}
}
return result
Expand Down Expand Up @@ -1387,12 +1364,6 @@ func (what TechnicalAsset) HighestConfidentiality() Confidentiality {
highest = dataAsset.Confidentiality
}
}
for _, dataId := range what.DataAssetsStored {
dataAsset := ParsedModelRoot.DataAssets[dataId]
if dataAsset.Confidentiality > highest {
highest = dataAsset.Confidentiality
}
}
return highest
}

Expand Down Expand Up @@ -1440,12 +1411,6 @@ func (what TechnicalAsset) HighestIntegrity() Criticality {
highest = dataAsset.Integrity
}
}
for _, dataId := range what.DataAssetsStored {
dataAsset := ParsedModelRoot.DataAssets[dataId]
if dataAsset.Integrity > highest {
highest = dataAsset.Integrity
}
}
return highest
}

Expand All @@ -1457,12 +1422,6 @@ func (what TechnicalAsset) HighestAvailability() Criticality {
highest = dataAsset.Availability
}
}
for _, dataId := range what.DataAssetsStored {
dataAsset := ParsedModelRoot.DataAssets[dataId]
if dataAsset.Availability > highest {
highest = dataAsset.Availability
}
}
return highest
}

Expand Down Expand Up @@ -2234,9 +2193,9 @@ func (what CommunicationLink) DetermineArrowLineStyle() string {
return "solid"
}

// dotted when model forgery attempt (i.e. nothing being processed or stored)
// dotted when model forgery attempt (i.e. nothing being processed)
func (what TechnicalAsset) DetermineShapeBorderLineStyle() string {
if len(what.DataAssetsProcessed) == 0 && len(what.DataAssetsStored) == 0 || what.OutOfScope {
if len(what.DataAssetsProcessed) == 0 || what.OutOfScope {
return "dotted" // dotted, because it's strange when too many technical communication links transfer no data... some ok, but many in a diagram ist a sign of model forgery...
}
return "solid"
Expand Down Expand Up @@ -2358,27 +2317,17 @@ func (what TechnicalAsset) IsZero() bool {
}

func (what TechnicalAsset) ProcessesOrStoresDataAsset(dataAssetId string) bool {
if Contains(what.DataAssetsProcessed, dataAssetId) {
return true
}
if Contains(what.DataAssetsStored, dataAssetId) {
return true
}
return false
return Contains(what.DataAssetsProcessed, dataAssetId)
}

// red when >= confidential data stored in unencrypted technical asset
// red when >= confidential data processed in unencrypted technical asset
// NOTE: maybe this should only check stored data, not processed data?
func (what TechnicalAsset) DetermineLabelColor() string {
// TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
// Check for red
if what.Integrity == MissionCritical {
return colors.Red
}
for _, storedDataAsset := range what.DataAssetsStored {
if ParsedModelRoot.DataAssets[storedDataAsset].Integrity == MissionCritical {
return colors.Red
}
}
for _, processedDataAsset := range what.DataAssetsProcessed {
if ParsedModelRoot.DataAssets[processedDataAsset].Integrity == MissionCritical {
return colors.Red
Expand All @@ -2388,11 +2337,6 @@ func (what TechnicalAsset) DetermineLabelColor() string {
if what.Integrity == Critical {
return colors.Amber
}
for _, storedDataAsset := range what.DataAssetsStored {
if ParsedModelRoot.DataAssets[storedDataAsset].Integrity == Critical {
return colors.Amber
}
}
for _, processedDataAsset := range what.DataAssetsProcessed {
if ParsedModelRoot.DataAssets[processedDataAsset].Integrity == Critical {
return colors.Amber
Expand Down Expand Up @@ -2426,18 +2370,13 @@ func (what TechnicalAsset) DetermineLabelColor() string {

// red when mission-critical integrity, but still unauthenticated (non-readonly) channels access it
// amber when critical integrity, but still unauthenticated (non-readonly) channels access it
// pink when model forgery attempt (i.e. nothing being processed or stored)
// pink when model forgery attempt (i.e. nothing being processed)
func (what TechnicalAsset) DetermineShapeBorderColor() string {
// TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
// Check for red
if what.Confidentiality == StrictlyConfidential {
return colors.Red
}
for _, storedDataAsset := range what.DataAssetsStored {
if ParsedModelRoot.DataAssets[storedDataAsset].Confidentiality == StrictlyConfidential {
return colors.Red
}
}
for _, processedDataAsset := range what.DataAssetsProcessed {
if ParsedModelRoot.DataAssets[processedDataAsset].Confidentiality == StrictlyConfidential {
return colors.Red
Expand All @@ -2447,11 +2386,6 @@ func (what TechnicalAsset) DetermineShapeBorderColor() string {
if what.Confidentiality == Confidential {
return colors.Amber
}
for _, storedDataAsset := range what.DataAssetsStored {
if ParsedModelRoot.DataAssets[storedDataAsset].Confidentiality == Confidential {
return colors.Amber
}
}
for _, processedDataAsset := range what.DataAssetsProcessed {
if ParsedModelRoot.DataAssets[processedDataAsset].Confidentiality == Confidential {
return colors.Amber
Expand Down Expand Up @@ -2587,7 +2521,7 @@ func (what CommunicationLink) DetermineArrowColor() string {

func (what TechnicalAsset) DetermineShapeFillColor() string {
fillColor := colors.VeryLightGray
if len(what.DataAssetsProcessed) == 0 && len(what.DataAssetsStored) == 0 ||
if len(what.DataAssetsProcessed) == 0 ||
what.Technology == UnknownTechnology {
fillColor = colors.LightPink // lightPink, because it's strange when too many technical assets process no data... some ok, but many in a diagram ist a sign of model forgery...
} else if len(what.CommunicationLinks) == 0 && len(IncomingTechnicalCommunicationLinksMappedByTargetId[what.Id]) == 0 {
Expand Down
2 changes: 2 additions & 0 deletions raa/raa/raa.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ func calculateAttackerAttractiveness(techAsset model.TechnicalAsset) float64 {
score += dataAsset.Integrity.AttackerAttractivenessForProcessedOrStoredData() * dataAsset.Quantity.QuantityFactor()
score += dataAsset.Availability.AttackerAttractivenessForProcessedOrStoredData()
}
// NOTE: Assuming all stored data is also processed, this effectively scores stored data twice
for _, dataAssetStored := range techAsset.DataAssetsStored {
dataAsset := model.ParsedModelRoot.DataAssets[dataAssetStored]
score += dataAsset.Confidentiality.AttackerAttractivenessForProcessedOrStoredData() * dataAsset.Quantity.QuantityFactor()
score += dataAsset.Integrity.AttackerAttractivenessForProcessedOrStoredData() * dataAsset.Quantity.QuantityFactor()
score += dataAsset.Availability.AttackerAttractivenessForProcessedOrStoredData()
}
// NOTE: To send or receive data effectively is processing that data and it's questionable if the attractiveness increases further
for _, dataFlow := range techAsset.CommunicationLinks {
for _, dataAssetSent := range dataFlow.DataAssetsSent {
dataAsset := model.ParsedModelRoot.DataAssets[dataAssetSent]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Category() model.RiskCategory {
Function: model.Operations,
STRIDE: model.InformationDisclosure,
DetectionLogic: "In-scope sourcecode repositories and artifact registries.",
RiskAssessment: "The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed and stored.",
RiskAssessment: "The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed.",
FalsePositives: "Usually no false positives.",
ModelFailurePossibleReason: false,
CWE: 200,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func Category() model.RiskCategory {
Function: model.Operations,
STRIDE: model.ElevationOfPrivilege,
DetectionLogic: "In-scope container platforms.",
RiskAssessment: "The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed and stored.",
RiskAssessment: "The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed.",
FalsePositives: "Container platforms not running parts of the target architecture can be considered " +
"as false positives after individual review.",
ModelFailurePossibleReason: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func Category() model.RiskCategory {
Function: model.Development,
STRIDE: model.Tampering,
DetectionLogic: "In-scope web applications.",
RiskAssessment: "The risk rating depends on the sensitivity of the data processed or stored in the web application.",
RiskAssessment: "The risk rating depends on the sensitivity of the data processed in the web application.",
FalsePositives: "When the technical asset " +
"is not accessed via a browser-like component (i.e not by a human user initiating the request that " +
"gets passed through all components until it reaches the web application) this can be considered a false positive.",
Expand Down
4 changes: 2 additions & 2 deletions risks/built-in/ldap-injection/ldap-injection-rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func Category() model.RiskCategory {
Id: "ldap-injection",
Title: "LDAP-Injection",
Description: "When an LDAP server is accessed LDAP-Injection risks might arise. " +
"The risk rating depends on the sensitivity of the LDAP server itself and of the data assets processed or stored.",
"The risk rating depends on the sensitivity of the LDAP server itself and of the data assets processed.",
Impact: "If this risk remains unmitigated, attackers might be able to modify LDAP queries and access more data from the LDAP server than allowed.",
ASVS: "V5 - Validation, Sanitization and Encoding Verification Requirements",
CheatSheet: "https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html",
Expand All @@ -21,7 +21,7 @@ func Category() model.RiskCategory {
Function: model.Development,
STRIDE: model.Tampering,
DetectionLogic: "In-scope clients accessing LDAP servers via typical LDAP access protocols.",
RiskAssessment: "The risk rating depends on the sensitivity of the LDAP server itself and of the data assets processed or stored.",
RiskAssessment: "The risk rating depends on the sensitivity of the LDAP server itself and of the data assets processed.",
FalsePositives: "LDAP server queries by search values not consisting of parts controllable by the caller can be considered " +
"as false positives after individual review.",
ModelFailurePossibleReason: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func Category() model.RiskCategory {
Id: "missing-authentication-second-factor",
Title: "Missing Two-Factor Authentication (2FA)",
Description: "Technical assets (especially multi-tenant systems) should authenticate incoming requests with " +
"two-factor (2FA) authentication when the asset processes or stores highly sensitive data (in terms of confidentiality, integrity, and availability) and is accessed by humans.",
"two-factor (2FA) authentication when the asset processes highly sensitive data (in terms of confidentiality, integrity, and availability) and is accessed by humans.",
Impact: "If this risk is unmitigated, attackers might be able to access or modify highly sensitive data without strong authentication.",
ASVS: "V2 - Authentication Verification Requirements",
CheatSheet: "https://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html",
Expand All @@ -21,7 +21,7 @@ func Category() model.RiskCategory {
Function: model.BusinessSide,
STRIDE: model.ElevationOfPrivilege,
DetectionLogic: "In-scope technical assets (except " + model.LoadBalancer.String() + ", " + model.ReverseProxy.String() + ", " + model.WAF.String() + ", " + model.IDS.String() + ", and " + model.IPS.String() + ") should authenticate incoming requests via two-factor authentication (2FA) " +
"when the asset processes or stores highly sensitive data (in terms of confidentiality, integrity, and availability) and is accessed by a client used by a human user.",
"when the asset processes highly sensitive data (in terms of confidentiality, integrity, and availability) and is accessed by a client used by a human user.",
RiskAssessment: model.MediumSeverity.String(),
FalsePositives: "Technical assets which do not process requests regarding functionality or data linked to end-users (customers) " +
"can be considered as false positives after individual review.",
Expand Down
Loading