@agent-ix/spec-objects-security 0.4.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/manifest.yaml +270 -26
- package/package.json +8 -2
- package/schemas/Asset.json +78 -0
- package/schemas/AttackSurface.json +55 -0
- package/schemas/AuditEvent.json +57 -0
- package/schemas/AuditFinding.json +67 -0
- package/schemas/AuthFlow.json +69 -0
- package/schemas/ConfidentialityLevel.json +13 -0
- package/schemas/Control.json +83 -0
- package/schemas/ControlEffectiveness.json +12 -0
- package/schemas/ControlMapping.json +26 -0
- package/schemas/CorsPolicy.json +46 -0
- package/schemas/CsrfToken.json +46 -0
- package/schemas/DataClassification.json +61 -0
- package/schemas/DefaultedField.json +14 -0
- package/schemas/EffectivenessField.json +15 -0
- package/schemas/EncryptionKey.json +78 -0
- package/schemas/FindingStatus.json +14 -0
- package/schemas/FlowStep.json +24 -0
- package/schemas/IdentityField.json +15 -0
- package/schemas/Impact.json +14 -0
- package/schemas/ImpactField.json +15 -0
- package/schemas/JwtClaim.json +49 -0
- package/schemas/LevelField.json +15 -0
- package/schemas/Likelihood.json +14 -0
- package/schemas/LikelihoodField.json +15 -0
- package/schemas/MfaFactorKind.json +12 -0
- package/schemas/MfaMethod.json +69 -0
- package/schemas/OccurrenceField.json +14 -0
- package/schemas/OccurrenceTypeRef.json +15 -0
- package/schemas/PasswordPolicy.json +40 -0
- package/schemas/Permission.json +49 -0
- package/schemas/Policy.json +52 -0
- package/schemas/Risk.json +79 -0
- package/schemas/Role.json +55 -0
- package/schemas/Scope.json +49 -0
- package/schemas/Secret.json +78 -0
- package/schemas/SecretLifecycle.json +14 -0
- package/schemas/SessionConfig.json +43 -0
- package/schemas/Severity.json +14 -0
- package/schemas/SeverityField.json +15 -0
- package/schemas/StatusField.json +15 -0
- package/schemas/StrideCategory.json +15 -0
- package/schemas/StrideCategoryField.json +15 -0
- package/schemas/Threat.json +67 -0
- package/schemas/TrustBoundary.json +69 -0
- package/schemas/TrustLevel.json +12 -0
- package/schemas/TrustLevelField.json +15 -0
- package/schemas/Vulnerability.json +70 -0
- package/schemas/toolchain.json +97 -0
- package/skeletons/asset.md +22 -14
- package/skeletons/asset.sysml.md +27 -0
- package/skeletons/attack_surface.md +21 -18
- package/skeletons/audit_event.md +31 -18
- package/skeletons/audit_finding.md +22 -18
- package/skeletons/auth_flow.md +56 -21
- package/skeletons/control.md +45 -16
- package/skeletons/cors_policy.md +33 -17
- package/skeletons/csrf_token.md +17 -13
- package/skeletons/data_classification.md +23 -18
- package/skeletons/encryption_key.md +19 -12
- package/skeletons/jwt_claim.md +25 -14
- package/skeletons/mfa_method.md +17 -11
- package/skeletons/password_policy.md +35 -20
- package/skeletons/permission.md +18 -12
- package/skeletons/policy.md +33 -17
- package/skeletons/risk.md +20 -14
- package/skeletons/role.md +20 -13
- package/skeletons/scope.md +18 -12
- package/skeletons/secret.md +23 -16
- package/skeletons/secret.sysml.md +29 -0
- package/skeletons/session_config.md +35 -18
- package/skeletons/threat.md +22 -11
- package/skeletons/threat.sysml.md +26 -0
- package/skeletons/trust_boundary.md +51 -16
- package/skeletons/vulnerability.md +18 -15
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
title: "
|
|
2
|
+
id: PWD-001
|
|
3
|
+
title: "TenantPasswordPolicy"
|
|
4
4
|
type: password_policy
|
|
5
|
+
object: password_policy
|
|
5
6
|
---
|
|
6
|
-
<!-- password_policy authoring skeleton (spec-objects-security). Fill every
|
|
7
|
-
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
password_policy
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Applies to break-glass local accounts only; regular tenant users authenticate
|
|
14
|
-
through their IdP (AUTHFLOW-001). Aligned with NIST SP 800-63B: length over
|
|
15
|
-
composition, breach screening over forced rotation.
|
|
7
|
+
<!-- password_policy authoring skeleton (spec-objects-security). Fill every section with
|
|
8
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
9
|
+
- Frontmatter MUST carry id, title, type: password_policy,
|
|
10
|
+
object: password_policy.
|
|
11
|
+
- "## Rules" (H2, required): the rules in prose.
|
|
12
|
+
- "## Invariants" (H2): PasswordPolicy.json requires at least one clause. -->
|
|
13
|
+
# [PWD-001] TenantPasswordPolicy
|
|
16
14
|
|
|
17
15
|
## Rules
|
|
18
16
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
- At least 12 characters, with no composition rule beyond length.
|
|
18
|
+
- Checked against the breached-password corpus at set time and at each login.
|
|
19
|
+
- No forced periodic expiry; rotation is event-driven on suspected compromise.
|
|
20
|
+
- Stored only as an Argon2id digest, never reversibly.
|
|
21
|
+
|
|
22
|
+
## Invariants
|
|
23
|
+
|
|
24
|
+
The clauses this policy asserts. Each clause owns one `ocl` fence under its
|
|
25
|
+
own `### <clauseId>` heading.
|
|
26
|
+
|
|
27
|
+
### MinimumLengthIsTwelve
|
|
28
|
+
|
|
29
|
+
```ocl
|
|
30
|
+
context TenantPasswordPolicy
|
|
31
|
+
inv MinimumLengthIsTwelve:
|
|
32
|
+
self.minimum_length >= 12
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### NoReversibleStorage
|
|
36
|
+
|
|
37
|
+
```ocl
|
|
38
|
+
context TenantPasswordPolicy
|
|
39
|
+
inv NoReversibleStorage:
|
|
40
|
+
self.storage_scheme = 'argon2id'
|
|
41
|
+
```
|
package/skeletons/permission.md
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: PERM-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "ExportTenantData"
|
|
4
4
|
type: permission
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
object: permission
|
|
6
|
+
resource: "tenant/records"
|
|
7
|
+
verb: export
|
|
7
8
|
---
|
|
8
|
-
<!-- permission authoring skeleton (spec-objects-security). Fill every
|
|
9
|
+
<!-- permission authoring skeleton (spec-objects-security). Fill every section with
|
|
9
10
|
substantive content. Contract (manifest body_extraction asserts):
|
|
10
|
-
- Frontmatter MUST carry id, title, type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
- Frontmatter MUST carry id, title, type: permission, object: permission,
|
|
12
|
+
resource (what is acted on) and verb (the action).
|
|
13
|
+
- "## Properties" (H2): the typed declaration. Permission.json forbids
|
|
14
|
+
`operations` and `relations`. -->
|
|
15
|
+
# [PERM-001] ExportTenantData
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
## Properties
|
|
18
|
+
|
|
19
|
+
| Field | Type | Multiplicity | Constraints |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| permission_id | UUID | 1..1 | identity |
|
|
22
|
+
| resource | String | 1..1 | minLength: 1 |
|
|
23
|
+
| verb | String | 1..1 | minLength: 1 |
|
|
24
|
+
| requires_reauthentication | Boolean | 1..1 | |
|
package/skeletons/policy.md
CHANGED
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: POL-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "LeastPrivilegeAccessPolicy"
|
|
4
4
|
type: policy
|
|
5
|
+
object: policy
|
|
5
6
|
---
|
|
6
|
-
<!-- policy authoring skeleton (spec-objects-security). Fill every
|
|
7
|
+
<!-- policy authoring skeleton (spec-objects-security). Fill every section with
|
|
7
8
|
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
and integration clients, owned by the security council.
|
|
9
|
+
- Frontmatter MUST carry id, title, type: policy, object: policy.
|
|
10
|
+
- "## Policy" (H2, required): the rule in prose.
|
|
11
|
+
- "## Invariants" (H2): Policy.json requires at least one clause — a
|
|
12
|
+
policy that constrains nothing is prose. -->
|
|
13
|
+
# [POL-001] LeastPrivilegeAccessPolicy
|
|
14
14
|
|
|
15
15
|
## Policy
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
Every principal holds the smallest set of permissions that lets it complete
|
|
18
|
+
its task, for the shortest time that task takes. A grant that is not exercised
|
|
19
|
+
within 90 days is revoked automatically, and every standing administrative
|
|
20
|
+
grant is reviewed quarterly by the tenant owner.
|
|
21
|
+
|
|
22
|
+
## Invariants
|
|
23
|
+
|
|
24
|
+
The clauses this policy asserts. Each clause owns one `ocl` fence under its
|
|
25
|
+
own `### <clauseId>` heading.
|
|
26
|
+
|
|
27
|
+
### NoStandingAdministrativeGrant
|
|
28
|
+
|
|
29
|
+
```ocl
|
|
30
|
+
context LeastPrivilegeAccessPolicy
|
|
31
|
+
inv NoStandingAdministrativeGrant:
|
|
32
|
+
TenantAdministrator.allInstances()->forAll(r | r.assignable_by_self_service = false)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### UnusedGrantsExpire
|
|
36
|
+
|
|
37
|
+
```ocl
|
|
38
|
+
context LeastPrivilegeAccessPolicy
|
|
39
|
+
inv UnusedGrantsExpire:
|
|
40
|
+
TenantReadScope.allInstances()->forAll(s | s.includes_write = false)
|
|
41
|
+
```
|
package/skeletons/risk.md
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: RISK-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "TenantDataExfiltration"
|
|
4
4
|
type: risk
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
object: risk
|
|
6
|
+
likelihood: possible
|
|
7
|
+
impact: major
|
|
7
8
|
---
|
|
8
|
-
<!-- risk authoring skeleton (spec-objects-security). Fill every
|
|
9
|
+
<!-- risk authoring skeleton (spec-objects-security). Fill every section with
|
|
9
10
|
substantive content. Contract (manifest body_extraction asserts):
|
|
10
|
-
- Frontmatter MUST carry id, title, type
|
|
11
|
-
likelihood and impact
|
|
12
|
-
|
|
11
|
+
- Frontmatter MUST carry id, title, type: risk, object: risk,
|
|
12
|
+
likelihood and impact.
|
|
13
|
+
- "## Properties" (H2): the typed declaration. Risk.json requires both a
|
|
14
|
+
`likelihood` row and an `impact` row: a risk scored on one axis is
|
|
15
|
+
not scored. -->
|
|
16
|
+
# [RISK-001] TenantDataExfiltration
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
## Properties
|
|
19
|
+
|
|
20
|
+
| Field | Type | Multiplicity | Constraints |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| risk_id | UUID | 1..1 | identity |
|
|
23
|
+
| likelihood | String | 1..1 | minLength: 1 |
|
|
24
|
+
| impact | String | 1..1 | minLength: 1 |
|
|
25
|
+
| assessed_at | Timestamp | 1..1 | |
|
|
26
|
+
| affected_asset | TenantDatabase | 0..1 | |
|
package/skeletons/role.md
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: ROLE-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "TenantAdministrator"
|
|
4
4
|
type: role
|
|
5
|
+
object: role
|
|
5
6
|
---
|
|
6
|
-
<!-- role authoring skeleton (spec-objects-security). Fill every
|
|
7
|
+
<!-- role authoring skeleton (spec-objects-security). Fill every section with
|
|
7
8
|
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Members can inspect configuration and audit trails but can never mutate state
|
|
14
|
-
or read secret material.
|
|
9
|
+
- Frontmatter MUST carry id, title, type: role, object: role.
|
|
10
|
+
- "## Permissions" (H2, required): the permissions this role grants.
|
|
11
|
+
- "## Properties" (H2): the typed declaration. A role is a grant set,
|
|
12
|
+
so Role.json forbids `operations`. -->
|
|
13
|
+
# [ROLE-001] TenantAdministrator
|
|
15
14
|
|
|
16
15
|
## Permissions
|
|
17
16
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
17
|
+
- ExportTenantData — export the tenant's records for portability requests.
|
|
18
|
+
- Manage tenant members, their roles and their MFA enrolment.
|
|
19
|
+
- Read the tenant audit trail; the role grants no write on it.
|
|
20
|
+
|
|
21
|
+
## Properties
|
|
22
|
+
|
|
23
|
+
| Field | Type | Multiplicity | Constraints |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| role_id | UUID | 1..1 | identity |
|
|
26
|
+
| name | String | 1..1 | minLength: 1, maxLength: 64 |
|
|
27
|
+
| granted_permission | ExportTenantData | 0..1 | |
|
|
28
|
+
| assignable_by_self_service | Boolean | 1..1 | |
|
package/skeletons/scope.md
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: SCOPE-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "TenantReadScope"
|
|
4
4
|
type: scope
|
|
5
|
+
object: scope
|
|
5
6
|
---
|
|
6
|
-
<!-- scope authoring skeleton (spec-objects-security). Fill every
|
|
7
|
+
<!-- scope authoring skeleton (spec-objects-security). Fill every section with
|
|
7
8
|
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
OAuth 2.0 scope requested by third-party integrations that need read-only
|
|
13
|
-
access to a tenant's document library through the Atlas public API.
|
|
9
|
+
- Frontmatter MUST carry id, title, type: scope, object: scope.
|
|
10
|
+
- "## Grants" (H2, required): what a bearer of this scope may do.
|
|
11
|
+
- "## Properties" (H2): the typed declaration. -->
|
|
12
|
+
# [SCOPE-001] TenantReadScope
|
|
14
13
|
|
|
15
14
|
## Grants
|
|
16
15
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
16
|
+
- Read access to the tenant's own records and audit trail.
|
|
17
|
+
- No write, no export, and no access to any other tenant.
|
|
18
|
+
- Downgrades to nothing when the grant family is revoked.
|
|
19
|
+
|
|
20
|
+
## Properties
|
|
21
|
+
|
|
22
|
+
| Field | Type | Multiplicity | Constraints |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| scope_id | UUID | 1..1 | identity |
|
|
25
|
+
| name | String | 1..1 | minLength: 1, maxLength: 64 |
|
|
26
|
+
| includes_write | Boolean | 1..1 | |
|
package/skeletons/secret.md
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: SECRET-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "AtlasOidcClientSecret"
|
|
4
4
|
type: secret
|
|
5
|
+
object: secret
|
|
5
6
|
---
|
|
6
|
-
<!-- secret authoring skeleton (spec-objects-security). Fill every
|
|
7
|
+
<!-- secret authoring skeleton (spec-objects-security). Fill every section with
|
|
7
8
|
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Vault at `secret/atlas/oidc/client-secret`; never present in environment files
|
|
15
|
-
or images.
|
|
9
|
+
- Frontmatter MUST carry id, title, type: secret, object: secret.
|
|
10
|
+
- "## Rotation" (H2, required): how and how often the secret rotates.
|
|
11
|
+
- "## Properties" (H2): the typed declaration. Secret.json admits no
|
|
12
|
+
field carrying a default, so the material can never be embedded here —
|
|
13
|
+
a secret is named by its locator only. -->
|
|
14
|
+
# [SECRET-001] AtlasOidcClientSecret
|
|
16
15
|
|
|
17
16
|
## Rotation
|
|
18
17
|
|
|
19
|
-
- Rotated every 90 days by the `secret-rotator` job, which registers
|
|
18
|
+
- Rotated every 90 days by the `secret-rotator` job, which registers the new
|
|
20
19
|
secret with the IdP before revoking the old one.
|
|
21
|
-
- Dual-secret overlap window of 24 hours so in-flight
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
- Dual-secret overlap window of 24 hours so in-flight exchanges complete.
|
|
21
|
+
- Emergency rotation completes in under 5 minutes.
|
|
22
|
+
- Every rotation emits a `secret.rotated` audit event.
|
|
23
|
+
|
|
24
|
+
## Properties
|
|
25
|
+
|
|
26
|
+
| Field | Type | Multiplicity | Constraints |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| secret_id | UUID | 1..1 | identity |
|
|
29
|
+
| locator | String | 1..1 | minLength: 1 |
|
|
30
|
+
| lifecycle | String | 1..1 | minLength: 1 |
|
|
31
|
+
| rotation_period | Duration | 1..1 | |
|
|
32
|
+
| overlap_window | Duration | 0..1 | |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SECRET-001
|
|
3
|
+
title: "AtlasOidcClientSecret"
|
|
4
|
+
type: secret
|
|
5
|
+
object: secret
|
|
6
|
+
---
|
|
7
|
+
<!-- secret authoring skeleton, alternate Properties form. Declares exactly the
|
|
8
|
+
same fields as secret.md, authored as one ```sysml``` fence instead of the
|
|
9
|
+
typed table (FR-005-AC-2). No row carries a value: a secret is named by
|
|
10
|
+
its locator only (FR-006). -->
|
|
11
|
+
# [SECRET-001] AtlasOidcClientSecret
|
|
12
|
+
|
|
13
|
+
## Rotation
|
|
14
|
+
|
|
15
|
+
- Rotated every 90 days by the `secret-rotator` job, which registers the new
|
|
16
|
+
secret with the IdP before revoking the old one.
|
|
17
|
+
- Dual-secret overlap window of 24 hours so in-flight exchanges complete.
|
|
18
|
+
- Emergency rotation completes in under 5 minutes.
|
|
19
|
+
- Every rotation emits a `secret.rotated` audit event.
|
|
20
|
+
|
|
21
|
+
## Properties
|
|
22
|
+
|
|
23
|
+
```sysml
|
|
24
|
+
attribute secret_id : UUID[1..1] { identity }
|
|
25
|
+
attribute locator : String[1..1] { minLength: 1 }
|
|
26
|
+
attribute lifecycle : String[1..1] { minLength: 1 }
|
|
27
|
+
attribute rotation_period : Duration[1..1]
|
|
28
|
+
attribute overlap_window : Duration[0..1]
|
|
29
|
+
```
|
|
@@ -1,24 +1,41 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
title: "
|
|
2
|
+
id: SESS-001
|
|
3
|
+
title: "TenantSessionConfig"
|
|
4
4
|
type: session_config
|
|
5
|
+
object: session_config
|
|
5
6
|
---
|
|
6
|
-
<!-- session_config authoring skeleton (spec-objects-security). Fill every
|
|
7
|
-
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
session_config
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Server-side session established after AUTHFLOW-001 completes. Session state
|
|
14
|
-
lives in the session store (ASSET-001); the browser holds only an opaque
|
|
15
|
-
identifier.
|
|
7
|
+
<!-- session_config authoring skeleton (spec-objects-security). Fill every section with
|
|
8
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
9
|
+
- Frontmatter MUST carry id, title, type: session_config,
|
|
10
|
+
object: session_config.
|
|
11
|
+
- "## Settings" (H2, required): the settings in prose.
|
|
12
|
+
- "## Invariants" (H2): SessionConfig.json requires at least one clause. -->
|
|
13
|
+
# [SESS-001] TenantSessionConfig
|
|
16
14
|
|
|
17
15
|
## Settings
|
|
18
16
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
- Idle timeout 30 minutes; absolute lifetime 12 hours.
|
|
18
|
+
- Cookie flags `HttpOnly`, `Secure`, `SameSite=Lax`, host-only.
|
|
19
|
+
- Session identifier rotates on every privilege change.
|
|
20
|
+
- Bound to AuthorizationCodeWithPkce; no session outlives its grant family.
|
|
21
|
+
|
|
22
|
+
## Invariants
|
|
23
|
+
|
|
24
|
+
The clauses this configuration asserts. Each clause owns one `ocl` fence under
|
|
25
|
+
its own `### <clauseId>` heading.
|
|
26
|
+
|
|
27
|
+
### IdleTimeoutIsShorterThanAbsoluteLifetime
|
|
28
|
+
|
|
29
|
+
```ocl
|
|
30
|
+
context TenantSessionConfig
|
|
31
|
+
inv IdleTimeoutIsShorterThanAbsoluteLifetime:
|
|
32
|
+
self.idle_timeout < self.absolute_lifetime
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### IdentifierRotatesOnPrivilegeChange
|
|
36
|
+
|
|
37
|
+
```ocl
|
|
38
|
+
context TenantSessionConfig
|
|
39
|
+
inv IdentifierRotatesOnPrivilegeChange:
|
|
40
|
+
self.rotate_on_privilege_change
|
|
41
|
+
```
|
package/skeletons/threat.md
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: THREAT-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "RefreshTokenReplay"
|
|
4
4
|
type: threat
|
|
5
|
+
object: threat
|
|
5
6
|
stride_category: Spoofing
|
|
6
7
|
vector: "exfiltrated refresh token replayed from attacker infrastructure"
|
|
7
8
|
---
|
|
8
|
-
<!-- threat authoring skeleton (spec-objects-security). Fill every
|
|
9
|
+
<!-- threat authoring skeleton (spec-objects-security). Fill every section with
|
|
9
10
|
substantive content. Contract (manifest body_extraction asserts):
|
|
10
|
-
- Frontmatter MUST carry id, title, type
|
|
11
|
-
stride_category (STRIDE class) and vector (the attack vector).
|
|
12
|
-
|
|
11
|
+
- Frontmatter MUST carry id, title, type: threat, object: threat,
|
|
12
|
+
stride_category (STRIDE class) and vector (the attack vector).
|
|
13
|
+
- "## Properties" (H2): the typed declaration. Threat.json requires an
|
|
14
|
+
identity row AND a `stride_category` row: a threat nobody classified
|
|
15
|
+
is not a threat statement. -->
|
|
16
|
+
# [THREAT-001] RefreshTokenReplay
|
|
13
17
|
|
|
14
|
-
An attacker who exfiltrates a refresh token
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
An attacker who exfiltrates a refresh token replays it against the token
|
|
19
|
+
endpoint to mint fresh access tokens and impersonate the user across tenant
|
|
20
|
+
APIs. Detection is hard because the requests are well formed; mitigation
|
|
21
|
+
relies on RefreshTokenRotation and the TenantNetworkEdge controls.
|
|
22
|
+
|
|
23
|
+
## Properties
|
|
24
|
+
|
|
25
|
+
| Field | Type | Multiplicity | Constraints |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| threat_id | UUID | 1..1 | identity |
|
|
28
|
+
| stride_category | String | 1..1 | minLength: 1 |
|
|
29
|
+
| vector | String | 1..1 | minLength: 1 |
|
|
30
|
+
| first_observed_at | Timestamp | 0..1 | |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: THREAT-001
|
|
3
|
+
title: "RefreshTokenReplay"
|
|
4
|
+
type: threat
|
|
5
|
+
object: threat
|
|
6
|
+
stride_category: Spoofing
|
|
7
|
+
vector: "exfiltrated refresh token replayed from attacker infrastructure"
|
|
8
|
+
---
|
|
9
|
+
<!-- threat authoring skeleton, alternate Properties form. Declares exactly the
|
|
10
|
+
same fields as threat.md, authored as one ```sysml``` fence instead of the
|
|
11
|
+
typed table (FR-005-AC-2). -->
|
|
12
|
+
# [THREAT-001] RefreshTokenReplay
|
|
13
|
+
|
|
14
|
+
An attacker who exfiltrates a refresh token replays it against the token
|
|
15
|
+
endpoint to mint fresh access tokens and impersonate the user across tenant
|
|
16
|
+
APIs. Detection is hard because the requests are well formed; mitigation
|
|
17
|
+
relies on RefreshTokenRotation and the TenantNetworkEdge controls.
|
|
18
|
+
|
|
19
|
+
## Properties
|
|
20
|
+
|
|
21
|
+
```sysml
|
|
22
|
+
attribute threat_id : UUID[1..1] { identity }
|
|
23
|
+
attribute stride_category : String[1..1] { minLength: 1 }
|
|
24
|
+
attribute vector : String[1..1] { minLength: 1 }
|
|
25
|
+
attribute first_observed_at : Timestamp[0..1]
|
|
26
|
+
```
|
|
@@ -1,26 +1,61 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: BOUND-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "TenantNetworkEdge"
|
|
4
4
|
type: trust_boundary
|
|
5
|
+
object: trust_boundary
|
|
5
6
|
---
|
|
6
|
-
<!-- trust_boundary authoring skeleton (spec-objects-security). Fill every
|
|
7
|
-
|
|
8
|
-
- Frontmatter MUST carry id, title, type
|
|
9
|
-
trust_boundary
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
private subnet where the Atlas auth service and session store run. Everything
|
|
16
|
-
crossing inward is re-authenticated: the gateway terminates TLS and the auth
|
|
17
|
-
service only accepts mTLS connections from the gateway.
|
|
7
|
+
<!-- trust_boundary authoring skeleton (spec-objects-security). Fill every section with
|
|
8
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
9
|
+
- Frontmatter MUST carry id, title, type: trust_boundary,
|
|
10
|
+
object: trust_boundary.
|
|
11
|
+
- "## Boundary" (H2, required) holds a fenced `mermaid` diagram.
|
|
12
|
+
- "## Properties" (H2): requires an identity row and a `trust_level` row.
|
|
13
|
+
- "## Invariants" (H2): TrustBoundary.json requires at least one clause —
|
|
14
|
+
a boundary that admits every crossing is not a boundary. -->
|
|
15
|
+
# [BOUND-001] TenantNetworkEdge
|
|
18
16
|
|
|
19
17
|
## Boundary
|
|
20
18
|
|
|
21
19
|
```mermaid
|
|
22
20
|
flowchart LR
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
subgraph untrusted[Internet]
|
|
22
|
+
client[Tenant browser]
|
|
23
|
+
end
|
|
24
|
+
subgraph trusted[Tenant VPC]
|
|
25
|
+
api[Token endpoint]
|
|
26
|
+
db[(Tenant database)]
|
|
27
|
+
end
|
|
28
|
+
client -->|TLS 1.3, authenticated| api
|
|
29
|
+
api --> db
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Properties
|
|
33
|
+
|
|
34
|
+
| Field | Type | Multiplicity | Constraints |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| boundary_id | UUID | 1..1 | identity |
|
|
37
|
+
| trust_level | String | 1..1 | minLength: 1 |
|
|
38
|
+
| ingress_protocol | String | 1..1 | minLength: 1 |
|
|
39
|
+
| mutual_tls_required | Boolean | 1..1 | |
|
|
40
|
+
|
|
41
|
+
## Invariants
|
|
42
|
+
|
|
43
|
+
The clauses this boundary enforces on every crossing. Each clause owns one
|
|
44
|
+
`ocl` fence under its own `### <clauseId>` heading; the fence text is carried
|
|
45
|
+
verbatim and never evaluated here.
|
|
46
|
+
|
|
47
|
+
### EveryCrossingIsAuthenticated
|
|
48
|
+
|
|
49
|
+
```ocl
|
|
50
|
+
context TenantNetworkEdge
|
|
51
|
+
inv EveryCrossingIsAuthenticated:
|
|
52
|
+
self.ingress_protocol <> 'plaintext'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### UntrustedSideNeverReachesTheDatabase
|
|
56
|
+
|
|
57
|
+
```ocl
|
|
58
|
+
context TenantNetworkEdge
|
|
59
|
+
inv UntrustedSideNeverReachesTheDatabase:
|
|
60
|
+
self.trust_level = 'untrusted' implies self.mutual_tls_required
|
|
26
61
|
```
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: VULN-001
|
|
3
|
-
title: "
|
|
3
|
+
title: "UnrotatedRefreshToken"
|
|
4
4
|
type: vulnerability
|
|
5
|
-
|
|
5
|
+
object: vulnerability
|
|
6
6
|
severity: high
|
|
7
|
+
cve_id: "CVE-2026-00000"
|
|
7
8
|
---
|
|
8
|
-
<!-- vulnerability authoring skeleton (spec-objects-security). Fill every
|
|
9
|
-
|
|
10
|
-
- Frontmatter MUST carry id, title, type
|
|
11
|
-
vulnerability
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
<!-- vulnerability authoring skeleton (spec-objects-security). Fill every section with
|
|
10
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
11
|
+
- Frontmatter MUST carry id, title, type: vulnerability,
|
|
12
|
+
object: vulnerability and severity; cve_id is optional.
|
|
13
|
+
- "## Properties" (H2): the typed declaration. Vulnerability.json
|
|
14
|
+
requires a `severity` row, so an unscored weakness is refused. -->
|
|
15
|
+
# [VULN-001] UnrotatedRefreshToken
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
## Properties
|
|
18
|
+
|
|
19
|
+
| Field | Type | Multiplicity | Constraints |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| vulnerability_id | UUID | 1..1 | identity |
|
|
22
|
+
| severity | String | 1..1 | minLength: 1 |
|
|
23
|
+
| affected_component | String | 1..1 | minLength: 1 |
|
|
24
|
+
| disclosed_at | Timestamp | 0..1 | |
|