001package gov.nist.secauto.oscal.lib.model; 002 003import gov.nist.secauto.metaschema.core.datatype.adapter.UuidAdapter; 004import gov.nist.secauto.metaschema.core.model.IBoundObject; 005import gov.nist.secauto.metaschema.core.model.IMetaschemaData; 006import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior; 007import gov.nist.secauto.metaschema.core.model.constraint.IConstraint; 008import gov.nist.secauto.metaschema.core.util.ObjectUtils; 009import gov.nist.secauto.metaschema.databind.model.annotations.AssemblyConstraints; 010import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly; 011import gov.nist.secauto.metaschema.databind.model.annotations.BoundField; 012import gov.nist.secauto.metaschema.databind.model.annotations.BoundFlag; 013import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs; 014import gov.nist.secauto.metaschema.databind.model.annotations.Index; 015import gov.nist.secauto.metaschema.databind.model.annotations.IsUnique; 016import gov.nist.secauto.metaschema.databind.model.annotations.KeyField; 017import gov.nist.secauto.metaschema.databind.model.annotations.Let; 018import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly; 019import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints; 020import java.lang.Override; 021import java.lang.String; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.UUID; 025import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 026import org.apache.commons.lang3.builder.ToStringStyle; 027 028/** 029 * A plan of action and milestones which identifies initial and residual risks, deviations, and disposition, such as those required by FedRAMP. 030 */ 031@MetaschemaAssembly( 032 formalName = "Plan of Action and Milestones (POA&M)", 033 description = "A plan of action and milestones which identifies initial and residual risks, deviations, and disposition, such as those required by FedRAMP.", 034 name = "plan-of-action-and-milestones", 035 moduleClass = OscalPoamModule.class, 036 rootName = "plan-of-action-and-milestones", 037 remarks = "Either an OSCAL-based SSP must be imported, or a unique system-id must be specified. Both may be present.", 038 valueConstraints = @ValueConstraints(lets = @Let(name = "all-imports", target = "recurse-depth('.[import-ap]/doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-ap/@href)))/assessment-plan|.[import-ssp]/doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-ssp/@href)))/system-security-plan|.[import-profile]/resolve-profile(doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-profile/@href))))/catalog')")), 039 modelConstraints = @AssemblyConstraints(index = {@Index(id = "oscal-poam-index-metadata-scoped-role-id", formalName = "In-Scope Role Identifiers", description = "An index of role identifiers that are in-scope for the plan-of-action-and-milestones model. Roles are collected from imported system-securtity-plans, which in turn includes referenced profiles and catalogs. For a given role @id, a locally declared role takes precedence over a role that is imported, the role that was last imported.", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/role ! map:entry(@id,.))?*", name = "index-imports-metadata-role-id", keyFields = @KeyField(target = "@id")), @Index(id = "oscal-poam-index-metadata-scoped-location-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/location ! map:entry(@uuid,.))?*", name = "index-imports-metadata-location-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-party-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/party ! map:entry(@uuid,.))?*", name = "index-imports-metadata-party-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-party-organization-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/party[@type='organization'] ! map:entry(@uuid,.))?*", name = "index-imports-metadata-party-organization-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-property-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports//prop[@uuid] ! map:entry(@uuid,.))?*", name = "index-imports-metadata-property-uuid", keyFields = @KeyField(target = "@uuid"))}, unique = {@IsUnique(id = "oscal-unique-document-id", formalName = "Unique Document Identifier", description = "Ensure all document identifiers have a unique combination of @scheme and value.", level = IConstraint.Level.ERROR, target = "document-id", keyFields = {@KeyField(target = "@scheme"), @KeyField}), @IsUnique(id = "oscal-unique-property-in-context-location", formalName = "Unique Properties", description = "Ensure all properties are unique for a given location using a unique combination of @ns, @name, @class. @group. and @value.", level = IConstraint.Level.ERROR, target = ".//prop", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@name"), @KeyField(target = "@ns"), @KeyField(target = "@class"), @KeyField(target = "@group"), @KeyField(target = "@value")}), @IsUnique(id = "oscal-unique-link-in-context-location", formalName = "Unique Links", description = "Ensure all links are unique for a given location using a unique combination of @href, @rel, and @media-type.", level = IConstraint.Level.ERROR, target = ".//link", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@href"), @KeyField(target = "@rel"), @KeyField(target = "@media-type")}), @IsUnique(id = "oscal-unique-responsibility-in-context-location", formalName = "Unique Responsibilities", description = "Ensure all responsible-roles and responsible-parties are unique for a given location using a unique combination of @role-id and the combination of @party-uuid values.", level = IConstraint.Level.ERROR, target = ".//(responsible-party|responsible-role)", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@role-id"), @KeyField(target = "@party-uuid")}, remarks = "Since `responsible-party` and `responsible-role` associate multiple `party-uuid` entries with a single `role-id`, each role-id must be referenced only once.")}) 040) 041public class PlanOfActionAndMilestones extends AbstractOscalInstance implements IBoundObject { 042 private final IMetaschemaData __metaschemaData; 043 044 /** 045 * "A <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#machine-oriented\">machine-oriented</a>, <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique\">globally unique</a> identifier with <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#instance\">instance</a>scope that can be used to reference this POA&M instance in <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#poam-identifiers\">this OSCAL instance</a>. This UUID should be assigned <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency\">per-subject</a>, which means it should be consistently used to identify the same subject across revisions of the document." 046 */ 047 @BoundFlag( 048 formalName = "POA&M Universally Unique Identifier", 049 description = "A [machine-oriented](https://pages.nist.gov/OSCAL/concepts/identifier-use/#machine-oriented), [globally unique](https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique) identifier with [instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#instance)scope that can be used to reference this POA\\&M instance in [this OSCAL instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#poam-identifiers). This UUID should be assigned [per-subject](https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency), which means it should be consistently used to identify the same subject across revisions of the document.", 050 name = "uuid", 051 required = true, 052 typeAdapter = UuidAdapter.class 053 ) 054 private UUID _uuid; 055 056 @BoundAssembly( 057 formalName = "Document Metadata", 058 description = "Provides information about the containing document, and defines concepts that are shared across the document.", 059 useName = "metadata", 060 minOccurs = 1 061 ) 062 private Metadata _metadata; 063 064 @BoundAssembly( 065 formalName = "Import System Security Plan", 066 description = "Used by the assessment plan and POA\\&M to import information about the system.", 067 useName = "import-ssp", 068 remarks = "Used by the POA\\&M to import information about the system." 069 ) 070 private ImportSsp _importSsp; 071 072 @BoundField( 073 formalName = "System Identification", 074 description = "A [human-oriented](https://pages.nist.gov/OSCAL/concepts/identifier-use/#human-oriented), [globally unique](https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique) identifier with [cross-instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#cross-instance) scope that can be used to reference this system identification property elsewhere in [this or other OSCAL instances](https://pages.nist.gov/OSCAL/concepts/identifier-use/#scope). When referencing an externally defined `system identification`, the `system identification` must be used in the context of the external / imported OSCAL instance (e.g., uri-reference). This string should be assigned [per-subject](https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency), which means it should be consistently used to identify the same system across revisions of the document.", 075 useName = "system-id" 076 ) 077 private SystemId _systemId; 078 079 @BoundAssembly( 080 formalName = "Local Definitions", 081 description = "Allows components, and inventory-items to be defined within the POA\\&M for circumstances where no OSCAL-based SSP exists, or is not delivered with the POA\\&M.", 082 useName = "local-definitions" 083 ) 084 private LocalDefinitions _localDefinitions; 085 086 @BoundAssembly( 087 formalName = "Observation", 088 description = "Describes an individual observation.", 089 useName = "observation", 090 maxOccurs = -1, 091 groupAs = @GroupAs(name = "observations", inJson = JsonGroupAsBehavior.LIST) 092 ) 093 private List<Observation> _observations; 094 095 @BoundAssembly( 096 formalName = "Identified Risk", 097 description = "An identified risk.", 098 useName = "risk", 099 maxOccurs = -1, 100 groupAs = @GroupAs(name = "risks", inJson = JsonGroupAsBehavior.LIST) 101 ) 102 private List<Risk> _risks; 103 104 @BoundAssembly( 105 formalName = "Finding", 106 description = "Describes an individual finding.", 107 useName = "finding", 108 maxOccurs = -1, 109 groupAs = @GroupAs(name = "findings", inJson = JsonGroupAsBehavior.LIST) 110 ) 111 private List<Finding> _findings; 112 113 @BoundAssembly( 114 formalName = "POA&M Item", 115 description = "Describes an individual POA\\&M item.", 116 useName = "poam-item", 117 minOccurs = 1, 118 maxOccurs = -1, 119 groupAs = @GroupAs(name = "poam-items", inJson = JsonGroupAsBehavior.LIST) 120 ) 121 private List<PoamItem> _poamItems; 122 123 @BoundAssembly( 124 formalName = "Back matter", 125 description = "A collection of resources that may be referenced from within the OSCAL document instance.", 126 useName = "back-matter" 127 ) 128 private BackMatter _backMatter; 129 130 public PlanOfActionAndMilestones() { 131 this(null); 132 } 133 134 public PlanOfActionAndMilestones(IMetaschemaData data) { 135 this.__metaschemaData = data; 136 } 137 138 @Override 139 public IMetaschemaData getMetaschemaData() { 140 return __metaschemaData; 141 } 142 143 public UUID getUuid() { 144 return _uuid; 145 } 146 147 public void setUuid(UUID value) { 148 _uuid = value; 149 } 150 151 public Metadata getMetadata() { 152 return _metadata; 153 } 154 155 public void setMetadata(Metadata value) { 156 _metadata = value; 157 } 158 159 public ImportSsp getImportSsp() { 160 return _importSsp; 161 } 162 163 public void setImportSsp(ImportSsp value) { 164 _importSsp = value; 165 } 166 167 public SystemId getSystemId() { 168 return _systemId; 169 } 170 171 public void setSystemId(SystemId value) { 172 _systemId = value; 173 } 174 175 public LocalDefinitions getLocalDefinitions() { 176 return _localDefinitions; 177 } 178 179 public void setLocalDefinitions(LocalDefinitions value) { 180 _localDefinitions = value; 181 } 182 183 public List<Observation> getObservations() { 184 return _observations; 185 } 186 187 public void setObservations(List<Observation> value) { 188 _observations = value; 189 } 190 191 /** 192 * Add a new {@link Observation} item to the underlying collection. 193 * @param item the item to add 194 * @return {@code true} 195 */ 196 public boolean addObservation(Observation item) { 197 Observation value = ObjectUtils.requireNonNull(item,"item cannot be null"); 198 if (_observations == null) { 199 _observations = new LinkedList<>(); 200 } 201 return _observations.add(value); 202 } 203 204 /** 205 * Remove the first matching {@link Observation} item from the underlying collection. 206 * @param item the item to remove 207 * @return {@code true} if the item was removed or {@code false} otherwise 208 */ 209 public boolean removeObservation(Observation item) { 210 Observation value = ObjectUtils.requireNonNull(item,"item cannot be null"); 211 return _observations != null && _observations.remove(value); 212 } 213 214 public List<Risk> getRisks() { 215 return _risks; 216 } 217 218 public void setRisks(List<Risk> value) { 219 _risks = value; 220 } 221 222 /** 223 * Add a new {@link Risk} item to the underlying collection. 224 * @param item the item to add 225 * @return {@code true} 226 */ 227 public boolean addRisk(Risk item) { 228 Risk value = ObjectUtils.requireNonNull(item,"item cannot be null"); 229 if (_risks == null) { 230 _risks = new LinkedList<>(); 231 } 232 return _risks.add(value); 233 } 234 235 /** 236 * Remove the first matching {@link Risk} item from the underlying collection. 237 * @param item the item to remove 238 * @return {@code true} if the item was removed or {@code false} otherwise 239 */ 240 public boolean removeRisk(Risk item) { 241 Risk value = ObjectUtils.requireNonNull(item,"item cannot be null"); 242 return _risks != null && _risks.remove(value); 243 } 244 245 public List<Finding> getFindings() { 246 return _findings; 247 } 248 249 public void setFindings(List<Finding> value) { 250 _findings = value; 251 } 252 253 /** 254 * Add a new {@link Finding} item to the underlying collection. 255 * @param item the item to add 256 * @return {@code true} 257 */ 258 public boolean addFinding(Finding item) { 259 Finding value = ObjectUtils.requireNonNull(item,"item cannot be null"); 260 if (_findings == null) { 261 _findings = new LinkedList<>(); 262 } 263 return _findings.add(value); 264 } 265 266 /** 267 * Remove the first matching {@link Finding} item from the underlying collection. 268 * @param item the item to remove 269 * @return {@code true} if the item was removed or {@code false} otherwise 270 */ 271 public boolean removeFinding(Finding item) { 272 Finding value = ObjectUtils.requireNonNull(item,"item cannot be null"); 273 return _findings != null && _findings.remove(value); 274 } 275 276 public List<PoamItem> getPoamItems() { 277 return _poamItems; 278 } 279 280 public void setPoamItems(List<PoamItem> value) { 281 _poamItems = value; 282 } 283 284 /** 285 * Add a new {@link PoamItem} item to the underlying collection. 286 * @param item the item to add 287 * @return {@code true} 288 */ 289 public boolean addPoamItem(PoamItem item) { 290 PoamItem value = ObjectUtils.requireNonNull(item,"item cannot be null"); 291 if (_poamItems == null) { 292 _poamItems = new LinkedList<>(); 293 } 294 return _poamItems.add(value); 295 } 296 297 /** 298 * Remove the first matching {@link PoamItem} item from the underlying collection. 299 * @param item the item to remove 300 * @return {@code true} if the item was removed or {@code false} otherwise 301 */ 302 public boolean removePoamItem(PoamItem item) { 303 PoamItem value = ObjectUtils.requireNonNull(item,"item cannot be null"); 304 return _poamItems != null && _poamItems.remove(value); 305 } 306 307 public BackMatter getBackMatter() { 308 return _backMatter; 309 } 310 311 public void setBackMatter(BackMatter value) { 312 _backMatter = value; 313 } 314 315 @Override 316 public String toString() { 317 return new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).toString(); 318 } 319}