Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .cards/local/calculations/defaultTitles/calculation.lp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ secdeva_defaultTitle("secdeva_review_project-role-assignments", "Role assignment
secdeva_defaultTitleWithDate("secdeva_review_project-structure", "Review of project structure - ").
secdeva_defaultTitleWithDate("secdeva_review_competence", "Competence review - ").
secdeva_defaultTitleWithDate("secdeva_review_periodic", "Periodic review - ").
secdeva_defaultTitleWithDate("secdeva_review_3rd-party-technologies", "Review of 3rd party technologies - ").
secdeva_defaultTitleWithDate("secdeva_review_3rd-party-technologies", "Review of third-party technologies - ").
secdeva_defaultTitleWithDate("secdeva_review_risk-management", "Review of risk management - ").
secdeva_defaultTitleWithDate("secdeva_review_interfaces", "Review of interfaces - "). % Legacy
secdeva_defaultTitleWithDate("secdeva_review_process-scoping", "Review of process scoping - ").
Expand Down
6 changes: 3 additions & 3 deletions .cards/local/calculations/evidence/calculation.lp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ secdeva_evidence("secdeva_control_security-requirements", "secdeva_register_secu
secdeva_legacyEvidence("secdeva_control_security-requirement-testing", "secdeva_test_security-requirements", "Review of \nsecurity requirement testing").
secdeva_evidence("secdeva_control_security-requirement-testing", "secdeva_review_security-requirement-testing", "Review of \nsecurity requirement testing").
secdeva_evidence("secdeva_control_3rd-party-technologies", "secdeva_register_technology-assessments", "Technology assessments").
secdeva_evidence("secdeva_control_3rd-party-technologies", "secdeva_review_3rd-party-technologies", "Review of 3rd party technologies").
secdeva_evidence("secdeva_control_vulnerability-management", "secdeva_review_vulnerability-management", "Review of 3rd party \nvulnerability management").
secdeva_evidence("secdeva_control_software-update-management", "secdeva_review_software-update-management", "Review of 3rd party \nsoftware update management").
secdeva_evidence("secdeva_control_3rd-party-technologies", "secdeva_review_3rd-party-technologies", "Review of third-party technologies").
secdeva_evidence("secdeva_control_vulnerability-management", "secdeva_review_vulnerability-management", "Review of third-party \nvulnerability management").
secdeva_evidence("secdeva_control_software-update-management", "secdeva_review_software-update-management", "Review of third-party \nsoftware update management").
secdeva_legacyEvidence("secdeva_control_use-of-cryptography", "secdeva_review_use-of-cryptography", "Review of \nthe use of cryptograpy").
secdeva_evidence("secdeva_control_use-of-cryptography", "secdeva_document_use-of-cryptography", "Use of cryptograpy document").
secdeva_evidence("secdeva_control_protect-environments", "secdeva_review_protect-environments", "Review of\nprotecting environments").
Expand Down
7 changes: 7 additions & 0 deletions .cards/local/calculations/product.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "secdeva/calculations/product",
"displayName": "Products",
"description": "Normalises the CSAF relationships between products and derives the planned supply chain role",
"calculation": "calculation.lp",
"category": "Solution structure"
}
236 changes: 236 additions & 0 deletions .cards/local/calculations/product/calculation.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
% The solution structure records what the solution is made of. Relationships between
% products are links, never parent and child cards: product composition is a graph,
% and a component that belongs to two products cannot have two parents.

% secdeva_csafRelationship(Component, Product, Kind, Label) means that Component relates
% to Product in the way named by Kind. Label is the human-readable form used in reports.

secdeva_csafRelationship(A, B, "defaultComponentOf", "built-in component of") :-
link(A, B, "secdeva/linkTypes/defaultComponentOf").
secdeva_csafRelationship(A, B, "externalComponentOf", "external dependency of") :-
link(A, B, "secdeva/linkTypes/externalComponentOf").
secdeva_csafRelationship(A, B, "installedOn", "installed on") :-
link(A, B, "secdeva/linkTypes/installedOn").
secdeva_csafRelationship(A, B, "installedWith", "installed alongside") :-
link(A, B, "secdeva/linkTypes/installedWith").
secdeva_csafRelationship(A, B, "optionalComponentOf", "optional add-on of") :-
link(A, B, "secdeva/linkTypes/optionalComponentOf").

% The same relationship reads differently from each end. The label in
% secdeva_csafRelationship describes the part ("optional add-on of X"); this one names
% what the part *is*, for tables that list the components of a product.

secdeva_csafPartLabel("defaultComponentOf", "built-in component").
secdeva_csafPartLabel("externalComponentOf", "external dependency").
secdeva_csafPartLabel("installedOn", "installed on this product").
secdeva_csafPartLabel("installedWith", "bundled alongside").
secdeva_csafPartLabel("optionalComponentOf", "optional add-on").

% A relationship that makes us responsible for placing a distinct product on the market.
% A default component is absorbed into the product that contains it, so it is excluded.

secdeva_distributableRelationship(P) :-
secdeva_csafRelationship(P, _, Kind, _),
Kind != "defaultComponentOf".

%
% The planned supply chain role
%

secdeva_placedOnMarketByUs(P) :-
base_projectCardType(P, "secdeva/cardTypes/product"),
not field(P, "secdeva/fieldTypes/productOrigin", "own"),
secdeva_distributableRelationship(P),
not link(P, _, "secdeva/linkTypes/defaultComponentOf").

% Both the role and its justification are calculated fields with an override, so they are
% emitted as fieldCalculated and the runtime suppresses either one as soon as the user
% sets it by hand.

secdeva_supplyChainRoleIsCalculated(P) :-
base_projectCardType(P, CardType),
calculatedField(CardType, "secdeva/fieldTypes/supplyChainRole"),
overridableField(CardType, "secdeva/fieldTypes/supplyChainRole").

secdeva_supplyChainRoleJustificationIsCalculated(P) :-
base_projectCardType(P, CardType),
calculatedField(CardType, "secdeva/fieldTypes/supplyChainRoleJustification"),
overridableField(CardType, "secdeva/fieldTypes/supplyChainRoleJustification").

% Each delivery model that places the product on the market names the role it produces
% and the reason for it.

secdeva_roleForDelivery("weDeliver", "distributor",
"We deliver a product that is already available on the market, which makes us its distributor.").
secdeva_roleForDelivery("weImportAndDeliver", "importer",
"We are the first to place this product on the market, from a manufacturer established outside it, which makes us its importer.").
secdeva_roleForDelivery("weRebrandAndDeliver", "manufacturer",
"We place this product on the market under our own name, or substantially modified, which makes us its manufacturer.").

% Delivery models under which the product is never placed on the market, so no supply
% chain role arises. weOperateAsService and weConsumeAsService differ in who runs the
% software, which matters a great deal for security management but does not change
% whether the product is placed on the market.

secdeva_notPlacedOnMarketReason("customerProvides",
"The customer provides this product. We depend on it but never place it on the market, so no supply chain role arises.").
secdeva_notPlacedOnMarketReason("weOperateAsService",
"We run this product ourselves to operate a service rather than shipping it to anyone, so it is not placed on the market and no supply chain role arises.").
secdeva_notPlacedOnMarketReason("weConsumeAsService",
"A third party operates this product and we use it over the network. We never run or ship it, so it is not placed on the market and no supply chain role arises.").

secdeva_notPlacedOnMarket(Delivery) :- secdeva_notPlacedOnMarketReason(Delivery, _).

% Developing a product ourselves makes us its manufacturer only if we actually place it
% on the market. A service we host is not placed on the market and carries no supply
% chain role. The role follows from the delivery model, so an own product with none
% recorded has no role yet, and the policy check below asks for one.

secdeva_ownProductNotPlacedOnMarket(P) :-
base_projectCardType(P, "secdeva/cardTypes/product"),
field(P, "secdeva/fieldTypes/productOrigin", "own"),
field(P, "secdeva/fieldTypes/deliveryModel", Delivery),
secdeva_notPlacedOnMarket(Delivery).

% secdeva_derivedRole(P, Role, Reason) pairs every derived role with the reason for it, so
% that the role and the justification shown beside it can never drift apart.

secdeva_derivedRole(P, Role, Reason) :-
secdeva_placedOnMarketByUs(P),
field(P, "secdeva/fieldTypes/deliveryModel", Delivery),
secdeva_roleForDelivery(Delivery, Role, Reason).

secdeva_derivedRole(P, "manufacturer",
"We develop this product ourselves and place it on the market, which makes us its manufacturer.") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
field(P, "secdeva/fieldTypes/productOrigin", "own"),
field(P, "secdeva/fieldTypes/deliveryModel", _),
not secdeva_ownProductNotPlacedOnMarket(P).

secdeva_derivedRole(P, "none", Reason) :-
secdeva_ownProductNotPlacedOnMarket(P),
field(P, "secdeva/fieldTypes/deliveryModel", Delivery),
secdeva_notPlacedOnMarketReason(Delivery, Reason).

secdeva_derivedRole(P, "integratedComponent",
"This third-party component is built into one of our own products, so our manufacturer obligations for that product cover it and no separate supply chain role arises.") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
not field(P, "secdeva/fieldTypes/productOrigin", "own"),
link(P, _, "secdeva/linkTypes/defaultComponentOf").

% A third-party product we neither run nor ship. Being a built-in component of one of our
% products takes precedence, so that case is excluded here rather than deriving two roles.

secdeva_derivedRole(P, "none", Reason) :-
base_projectCardType(P, "secdeva/cardTypes/product"),
not field(P, "secdeva/fieldTypes/productOrigin", "own"),
not link(P, _, "secdeva/linkTypes/defaultComponentOf"),
field(P, "secdeva/fieldTypes/deliveryModel", Delivery),
secdeva_notPlacedOnMarketReason(Delivery, Reason).

fieldCalculated(P, "secdeva/fieldTypes/supplyChainRole", Role) :-
secdeva_supplyChainRoleIsCalculated(P),
secdeva_derivedRole(P, Role, _).

fieldCalculated(P, "secdeva/fieldTypes/supplyChainRoleJustification", Reason) :-
secdeva_supplyChainRoleJustificationIsCalculated(P),
secdeva_derivedRole(P, _, Reason).

% Which group a product belongs to follows from its origin field, not from where the
% card happens to sit in the tree. The grouping cards are browsing containers and a
% target for the create buttons; they are not what decides anything.

secdeva_productGroup(Product, "secdeva_group_own-products") :-
base_projectCardType(Product, "secdeva/cardTypes/product"),
field(Product, "secdeva/fieldTypes/productOrigin", "own").

secdeva_productGroup(Product, "secdeva_group_third-party-products") :-
base_projectCardType(Product, "secdeva/cardTypes/product"),
field(Product, "secdeva/fieldTypes/productOrigin", Origin),
Origin != "own".

%
% Policy checks
%

policyCheckFailure(
P, "Solution structure", "Origin is a required field",
"Record whether this is an own product or a third-party product.") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
not field(P, "workflowStateCategory", "initial"),
not field(P, "secdeva/fieldTypes/productOrigin", _).

policyCheckFailure(
P, "Solution structure", "Delivery model is a required field",
"Record what we do with this product, including who is responsible for operating it. The planned supply chain role is derived from it.") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
not field(P, "workflowStateCategory", "initial"),
not field(P, "secdeva/fieldTypes/deliveryModel", _).

policyCheckFailure(
P, "Solution structure",
"Justification not given for an overridden supply chain role",
"The supply chain role has been set by hand, so the derived justification no longer describes it. Explain the role by overriding the supply chain role justification field.",
"secdeva/fieldTypes/supplyChainRoleJustification") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
fieldOverride(P, "secdeva/fieldTypes/supplyChainRole", _),
not fieldOverride(P, "secdeva/fieldTypes/supplyChainRoleJustification", _).

%
% Policy checks: the delivery model and the product relationships must agree
%

% A product that we never hold as an artefact cannot be packaged into our delivery. The
% customer's own copy and a service that someone else operates are both outside our
% hands, so neither can be built into a product we ship or bundled alongside one.
% weOperateAsService is deliberately absent: we do run that software, we just never ship it.

secdeva_neverInOurHands("customerProvides").
secdeva_neverInOurHands("weConsumeAsService").

secdeva_packagedRelationship("defaultComponentOf", "a built-in component of another product").
secdeva_packagedRelationship("installedWith", "bundled alongside another product").

policyCheckFailure(
A, "Solution structure",
"The delivery model contradicts a product relationship",
@concatenate(
"This product is recorded as ", RelationText,
", which means we ship it, but its delivery model says \"", Display,
"\". Correct the delivery model, or record the relationship as an external dependency instead."),
"secdeva/fieldTypes/deliveryModel") :-
base_projectCardType(A, "secdeva/cardTypes/product"),
secdeva_csafRelationship(A, _, Kind, _),
secdeva_packagedRelationship(Kind, RelationText),
field(A, "secdeva/fieldTypes/deliveryModel", Delivery),
secdeva_neverInOurHands(Delivery),
field(("secdeva/fieldTypes/deliveryModel", Delivery), "enumDisplayValue", Display).

% A service that a third party operates is not installed by us onto anything. The reverse
% is the ordinary platform-as-a-service case and must not be flagged: our own product
% installed on a platform that somebody else runs is exactly right.

policyCheckFailure(
A, "Solution structure",
"A service we consume cannot be installed on a platform",
"This product is recorded as installed on another product, but a third party operates it and we only use it over the network. Record it as an external dependency, or reverse the platform relationship.",
"secdeva/fieldTypes/deliveryModel") :-
base_projectCardType(A, "secdeva/cardTypes/product"),
link(A, _, "secdeva/linkTypes/installedOn"),
field(A, "secdeva/fieldTypes/deliveryModel", "weConsumeAsService").

% Developing a product is something only our own organisation does.

policyCheckFailure(
P, "Solution structure",
"Only an own product can be developed by us",
"The delivery model says we develop this product and place it on the market, but its origin says it comes from a third party. Correct the origin, or choose the delivery model that describes what we do with it.",
"secdeva/fieldTypes/deliveryModel") :-
base_projectCardType(P, "secdeva/cardTypes/product"),
field(P, "secdeva/fieldTypes/deliveryModel", "weDevelopAndDeliver"),
not field(P, "secdeva/fieldTypes/productOrigin", "own").

% Products are assets of the project.

ismsa_asset(P, "Product") :- base_projectCardType(P, "secdeva/cardTypes/product").
ismsa_disableAssetControl(P) :- base_projectCardType(P, "secdeva/cardTypes/product").
20 changes: 20 additions & 0 deletions .cards/local/calculations/registers/calculation.lp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ secdeva_registerMember(Asset, Register) :-
secdeva_closestProject(Asset, Project),
base_projectCardType(Asset, "secdeva/cardTypes/interface").

% members of the solution structure register

secdeva_registerMember(Product, Register) :-
secdeva_closestProject(Register, Project),
field(Register, "base/fieldTypes/identifier", "secdeva_register_solution-structure"),
secdeva_closestProject(Product, Project),
base_projectCardType(Product, "secdeva/cardTypes/product").

% members of the own and third-party product groups
%
% These are grouping cards inside the solution structure register. Membership follows
% the origin of the product rather than the position of the card, so a product filed
% under the wrong group still counts towards the right one.

secdeva_registerMember(Product, Group) :-
secdeva_closestProject(Group, Project),
field(Group, "base/fieldTypes/identifier", GroupIdentifier),
secdeva_closestProject(Product, Project),
secdeva_productGroup(Product, GroupIdentifier).

% members of the risk register

secdeva_registerMember(Risk, Register) :-
Expand Down
5 changes: 3 additions & 2 deletions .cards/local/cardTypes/dataflowModel.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"secdeva/fieldTypes/description"
],
"optionallyVisibleFields": [],
"displayName": "Dataflow model",
"description": "Top-level container for dataflow model elements"
"displayName": "Dataflow model (deprecated)",
"description": "Top-level container for dataflow model elements",
"category": "Deprecated"
}
Loading