@agent-ix/spec-objects-security 0.5.0 → 0.9.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.
Files changed (76) hide show
  1. package/README.md +2 -0
  2. package/manifest.yaml +247 -26
  3. package/package.json +8 -2
  4. package/schemas/Asset.json +78 -0
  5. package/schemas/AttackSurface.json +55 -0
  6. package/schemas/AuditEvent.json +57 -0
  7. package/schemas/AuditFinding.json +67 -0
  8. package/schemas/AuthFlow.json +69 -0
  9. package/schemas/ConfidentialityLevel.json +13 -0
  10. package/schemas/Control.json +83 -0
  11. package/schemas/ControlEffectiveness.json +12 -0
  12. package/schemas/ControlMapping.json +26 -0
  13. package/schemas/CorsPolicy.json +46 -0
  14. package/schemas/CsrfToken.json +46 -0
  15. package/schemas/DataClassification.json +61 -0
  16. package/schemas/DefaultedField.json +14 -0
  17. package/schemas/EffectivenessField.json +15 -0
  18. package/schemas/EncryptionKey.json +78 -0
  19. package/schemas/FindingStatus.json +14 -0
  20. package/schemas/FlowStep.json +24 -0
  21. package/schemas/IdentityField.json +15 -0
  22. package/schemas/Impact.json +14 -0
  23. package/schemas/ImpactField.json +15 -0
  24. package/schemas/JwtClaim.json +49 -0
  25. package/schemas/LevelField.json +15 -0
  26. package/schemas/Likelihood.json +14 -0
  27. package/schemas/LikelihoodField.json +15 -0
  28. package/schemas/MfaFactorKind.json +12 -0
  29. package/schemas/MfaMethod.json +69 -0
  30. package/schemas/OccurrenceField.json +14 -0
  31. package/schemas/OccurrenceTypeRef.json +15 -0
  32. package/schemas/PasswordPolicy.json +40 -0
  33. package/schemas/Permission.json +49 -0
  34. package/schemas/Policy.json +52 -0
  35. package/schemas/Risk.json +79 -0
  36. package/schemas/Role.json +55 -0
  37. package/schemas/Scope.json +49 -0
  38. package/schemas/Secret.json +78 -0
  39. package/schemas/SecretLifecycle.json +14 -0
  40. package/schemas/SessionConfig.json +43 -0
  41. package/schemas/Severity.json +14 -0
  42. package/schemas/SeverityField.json +15 -0
  43. package/schemas/StatusField.json +15 -0
  44. package/schemas/StrideCategory.json +15 -0
  45. package/schemas/StrideCategoryField.json +15 -0
  46. package/schemas/Threat.json +67 -0
  47. package/schemas/TrustBoundary.json +69 -0
  48. package/schemas/TrustLevel.json +12 -0
  49. package/schemas/TrustLevelField.json +15 -0
  50. package/schemas/Vulnerability.json +70 -0
  51. package/skeletons/asset.md +22 -14
  52. package/skeletons/asset.sysml.md +27 -0
  53. package/skeletons/attack_surface.md +21 -18
  54. package/skeletons/audit_event.md +31 -18
  55. package/skeletons/audit_finding.md +22 -18
  56. package/skeletons/auth_flow.md +56 -21
  57. package/skeletons/control.md +45 -16
  58. package/skeletons/cors_policy.md +33 -17
  59. package/skeletons/csrf_token.md +17 -13
  60. package/skeletons/data_classification.md +23 -18
  61. package/skeletons/encryption_key.md +19 -12
  62. package/skeletons/jwt_claim.md +25 -14
  63. package/skeletons/mfa_method.md +17 -11
  64. package/skeletons/password_policy.md +35 -20
  65. package/skeletons/permission.md +18 -12
  66. package/skeletons/policy.md +33 -17
  67. package/skeletons/risk.md +20 -14
  68. package/skeletons/role.md +20 -13
  69. package/skeletons/scope.md +18 -12
  70. package/skeletons/secret.md +23 -16
  71. package/skeletons/secret.sysml.md +29 -0
  72. package/skeletons/session_config.md +35 -18
  73. package/skeletons/threat.md +22 -11
  74. package/skeletons/threat.sysml.md +26 -0
  75. package/skeletons/trust_boundary.md +51 -16
  76. package/skeletons/vulnerability.md +18 -15
@@ -1,25 +1,29 @@
1
1
  ---
2
2
  id: FIND-001
3
- title: "Session store reachable with eval privileges"
3
+ title: "MissingTokenRotation"
4
4
  type: audit_finding
5
+ object: audit_finding
5
6
  ---
6
- <!-- audit_finding authoring skeleton (spec-objects-security). Fill every part
7
- with substantive content. Contract (manifest body_extraction asserts):
8
- - Frontmatter MUST carry id, title, type (type:
9
- audit_finding).
10
- - A "Recommendation" section MUST state the remediation recommendation. -->
11
- # [FIND-001] Session store reachable with eval privileges
12
-
13
- Q2 security audit finding: the auth service connects to the session store
14
- (ASSET-001) with a credential that retains `EVAL`/`SCRIPT` privileges,
15
- unnecessarily exposing the Lua sandbox surface of VULN-001 to any service
16
- compromise. Severity high; affected component is the shared Redis ACL profile.
7
+ <!-- audit_finding authoring skeleton (spec-objects-security). Fill every section with
8
+ substantive content. Contract (manifest body_extraction asserts):
9
+ - Frontmatter MUST carry id, title, type: audit_finding,
10
+ object: audit_finding.
11
+ - "## Recommendation" (H2, required): what the auditor asks for.
12
+ - "## Properties" (H2): the typed declaration. AuditFinding.json
13
+ requires a `status` row, so an untriaged finding is refused. -->
14
+ # [FIND-001] MissingTokenRotation
17
15
 
18
16
  ## Recommendation
19
17
 
20
- Upgrade the session store to a release patched for CVE-2024-31449, then
21
- replace the shared ACL profile with a least-privilege profile that removes
22
- `EVAL`, `EVALSHA` and `SCRIPT` for application credentials. Verify with an
23
- integration test that script commands are rejected, and re-scan the cluster.
24
- Target closure within 30 days; interim compensating control is the mTLS
25
- restriction at BOUND-001.
18
+ Enable refresh-token rotation with reuse detection on every tenant IdP
19
+ integration, and fail the release gate while any integration is without it.
20
+ Re-audit thirty days after the change lands.
21
+
22
+ ## Properties
23
+
24
+ | Field | Type | Multiplicity | Constraints |
25
+ |---|---|---|---|
26
+ | finding_id | UUID | 1..1 | identity |
27
+ | status | String | 1..1 | minLength: 1 |
28
+ | observed_at | Timestamp | 1..1 | |
29
+ | traced_risk | TenantDataExfiltration | 0..1 | |
@@ -1,31 +1,66 @@
1
1
  ---
2
- id: AUTHFLOW-001
3
- title: "Tenant user login via OIDC authorization code flow"
2
+ id: AUTH-001
3
+ title: "AuthorizationCodeWithPkce"
4
4
  type: auth_flow
5
+ object: auth_flow
5
6
  ---
6
- <!-- auth_flow authoring skeleton (spec-objects-security). Fill every part with
7
+ <!-- auth_flow 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 (type: auth_flow).
9
- - A "Flow" section MUST carry a ```mermaid code block describing the flow. -->
10
- # [AUTHFLOW-001] Tenant user login via OIDC authorization code flow
11
-
12
- Interactive browser login for Atlas tenant users. The platform delegates
13
- authentication to the tenant's OIDC identity provider using the authorization
14
- code flow with PKCE, then establishes a first-party session cookie.
9
+ - Frontmatter MUST carry id, title, type: auth_flow, object: auth_flow.
10
+ - "## Flow" (H2, required) holds a fenced `mermaid` diagram.
11
+ - "## Operations" (H2): one `### <name>` per exchange, an optional param
12
+ table, a `Returns: <Type>[<multiplicity>]` line and optional Pre:/Post:
13
+ lines. AuthFlow.json requires at least one operation — a flow is its
14
+ exchanges. -->
15
+ # [AUTH-001] AuthorizationCodeWithPkce
15
16
 
16
17
  ## Flow
17
18
 
18
19
  ```mermaid
19
20
  sequenceDiagram
20
- participant Browser
21
- participant AtlasApp
22
- participant IdP
23
- Browser->>AtlasApp: GET /login
24
- AtlasApp->>Browser: 302 to IdP authorize endpoint with PKCE challenge
25
- Browser->>IdP: GET /authorize with client_id and code_challenge
26
- IdP->>Browser: 302 back with authorization code
27
- Browser->>AtlasApp: GET /callback with code and state
28
- AtlasApp->>IdP: POST /token with code and code_verifier
29
- IdP->>AtlasApp: ID token plus access and refresh tokens
30
- AtlasApp->>Browser: Set __Host-atlas_session cookie and redirect
21
+ participant U as User agent
22
+ participant C as Client
23
+ participant A as Authorization server
24
+ C->>A: authorize with code_challenge
25
+ A->>U: prompt and consent
26
+ U->>C: authorization code
27
+ C->>A: exchange code with code_verifier
28
+ A->>C: access token and refresh token
31
29
  ```
30
+
31
+ ## Operations
32
+
33
+ The exchanges this flow performs. Each operation owns one `### <name>`
34
+ heading; the param table is the typed declaration of its inputs.
35
+
36
+ ### authorize
37
+
38
+ Start the flow and obtain an authorization code.
39
+
40
+ | Param | Type | Multiplicity | Constraints |
41
+ |---|---|---|---|
42
+ | code_challenge | String | 1..1 | minLength: 43 |
43
+ | redirect_uri | String | 1..1 | minLength: 1 |
44
+
45
+ Returns: String[1..1]
46
+
47
+ ### exchange_code
48
+
49
+ Exchange the code and verifier for a token pair.
50
+
51
+ | Param | Type | Multiplicity | Constraints |
52
+ |---|---|---|---|
53
+ | code | String | 1..1 | minLength: 1 |
54
+ | code_verifier | String | 1..1 | minLength: 43 |
55
+
56
+ Returns: Boolean[1..1]
57
+
58
+ ### refresh
59
+
60
+ Rotate the grant family and issue a fresh token pair.
61
+
62
+ | Param | Type | Multiplicity | Constraints |
63
+ |---|---|---|---|
64
+ | presented_token_locator | String | 1..1 | minLength: 1 |
65
+
66
+ Returns: Boolean[1..1]
@@ -1,24 +1,53 @@
1
1
  ---
2
2
  id: CTRL-001
3
- title: "Refresh token rotation with reuse detection"
3
+ title: "RefreshTokenRotation"
4
4
  type: control
5
+ object: control
5
6
  ---
6
- <!-- control authoring skeleton (spec-objects-security). Fill every part with
7
+ <!-- control 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 (type: control).
9
- - A "Mappings" section MUST map the control to threats, risks and
10
- standards it addresses. -->
11
- # [CTRL-001] Refresh token rotation with reuse detection
12
-
13
- Every refresh-token exchange invalidates the presented token and issues a new
14
- one bound to the same grant family. Presenting an already-rotated token marks
15
- the whole family compromised: all descendants are revoked, the session is
16
- terminated, and a `token.reuse.detected` audit event fires.
9
+ - Frontmatter MUST carry id, title, type: control, object: control.
10
+ - "## Mappings" (H2, required): the threats, risks and standards this
11
+ control addresses.
12
+ - "## Properties" (H2): Control.json requires an `effectiveness` row, so
13
+ effectiveness is always stated and never assumed.
14
+ - "## Invariants" (H2): Control.json requires at least one clause. -->
15
+ # [CTRL-001] RefreshTokenRotation
17
16
 
18
17
  ## Mappings
19
18
 
20
- - Mitigates THREAT-001 (stolen refresh token replay)
21
- - Reduces RISK-001 likelihood from medium to low
22
- - Implements OAuth 2.0 Security BCP (RFC 9700) refresh-token rotation guidance
23
- - Maps to OWASP ASVS v4 §3.5.2 and NIST SP 800-63B §7.1 session binding
24
- - Verified by integration test IT-001 (manifest activation roundtrip suite)
19
+ - Mitigates RefreshTokenReplay (stolen refresh token replay).
20
+ - Reduces TenantDataExfiltration likelihood from possible to unlikely.
21
+ - Implements OAuth 2.0 Security BCP (RFC 9700) refresh-token rotation guidance.
22
+ - Maps to OWASP ASVS v4 3.5.2 and NIST SP 800-63B 7.1 session binding.
23
+
24
+ ## Properties
25
+
26
+ | Field | Type | Multiplicity | Constraints |
27
+ |---|---|---|---|
28
+ | control_id | UUID | 1..1 | identity |
29
+ | effectiveness | String | 1..1 | minLength: 1 |
30
+ | owner | String | 1..1 | minLength: 1 |
31
+ | last_assessed_at | Timestamp | 1..1 | |
32
+
33
+ ## Invariants
34
+
35
+ The clauses this control enforces. Each clause owns one `ocl` fence under its
36
+ own `### <clauseId>` heading; the fence text is carried verbatim and never
37
+ evaluated here.
38
+
39
+ ### EveryExchangeInvalidatesThePresentedToken
40
+
41
+ ```ocl
42
+ context RefreshTokenRotation
43
+ inv EveryExchangeInvalidatesThePresentedToken:
44
+ self.effectiveness <> 'not_assessed' implies self.last_assessed_at->notEmpty()
45
+ ```
46
+
47
+ ### ReuseRevokesTheWholeFamily
48
+
49
+ ```ocl
50
+ context RefreshTokenRotation
51
+ inv ReuseRevokesTheWholeFamily:
52
+ self.owner->notEmpty()
53
+ ```
@@ -1,24 +1,40 @@
1
1
  ---
2
2
  id: CORS-001
3
- title: "Atlas public API CORS policy"
3
+ title: "TenantApiCorsPolicy"
4
4
  type: cors_policy
5
+ object: cors_policy
5
6
  ---
6
- <!-- cors_policy authoring skeleton (spec-objects-security). Fill every part
7
- with substantive content. Contract (manifest body_extraction asserts):
8
- - Frontmatter MUST carry id, title, type (type:
9
- cors_policy).
10
- - An "Origins" section MUST enumerate the allowed origins and rules. -->
11
- # [CORS-001] Atlas public API CORS policy
12
-
13
- Browser cross-origin policy enforced by the API gateway for `api.atlas.example`.
14
- Credentials are only honored for first-party app origins; integration origins
15
- get token-based access without cookies.
7
+ <!-- cors_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: cors_policy,
10
+ object: cors_policy.
11
+ - "## Origins" (H2, required): the allowed origins.
12
+ - "## Invariants" (H2): CorsPolicy.json requires at least one clause. -->
13
+ # [CORS-001] TenantApiCorsPolicy
16
14
 
17
15
  ## Origins
18
16
 
19
- - `https://app.atlas.example` — first-party web client; credentials allowed
20
- - `https://admin.atlas.example` — operator console; credentials allowed
21
- - `https://*.integrations.atlas.example` — sandboxed integration iframes;
22
- credentials disallowed, `Authorization` header only
23
- - All other origins are denied; no wildcard `*` is ever emitted, and preflight
24
- responses are capped at `Access-Control-Max-Age: 600`
17
+ - `https://app.example.com` — the first-party tenant console.
18
+ - `https://*.tenant.example.com` — per-tenant vanity hosts, exact-suffix matched.
19
+ - No wildcard origin, and no origin is echoed back unvalidated.
20
+
21
+ ## Invariants
22
+
23
+ The clauses this policy asserts. Each clause owns one `ocl` fence under its
24
+ own `### <clauseId>` heading.
25
+
26
+ ### NoWildcardOrigin
27
+
28
+ ```ocl
29
+ context TenantApiCorsPolicy
30
+ inv NoWildcardOrigin:
31
+ self.allowed_origins->excludes('*')
32
+ ```
33
+
34
+ ### CredentialsNeverWithWildcard
35
+
36
+ ```ocl
37
+ context TenantApiCorsPolicy
38
+ inv CredentialsNeverWithWildcard:
39
+ self.allow_credentials implies self.allowed_origins->size() > 0
40
+ ```
@@ -1,19 +1,23 @@
1
1
  ---
2
2
  id: CSRF-001
3
- title: "Atlas web CSRF token"
3
+ title: "SessionAntiForgeryToken"
4
4
  type: csrf_token
5
- rotation_window: 30m
5
+ object: csrf_token
6
+ rotation_window: PT30M
6
7
  ---
7
- <!-- csrf_token authoring skeleton (spec-objects-security). Fill every part with
8
+ <!-- csrf_token authoring skeleton (spec-objects-security). Fill every section with
8
9
  substantive content. Contract (manifest body_extraction asserts):
9
- - Frontmatter MUST carry id, title, type (type:
10
- csrf_token) and rotation_window (how often a fresh token is issued). -->
11
- # [CSRF-001] Atlas web CSRF token
10
+ - Frontmatter MUST carry id, title, type: csrf_token, object: csrf_token
11
+ and rotation_window.
12
+ - "## Properties" (H2): CsrfToken.json admits no field carrying a
13
+ default, so no token value appears in the declaration. -->
14
+ # [CSRF-001] SessionAntiForgeryToken
12
15
 
13
- Double-submit CSRF token protecting all state-changing requests from the Atlas
14
- web client. The server issues an HMAC-signed token bound to the session id
15
- (SESSION-001); the client echoes it in the `X-Atlas-CSRF` header and the
16
- middleware rejects any mutation where header and cookie values disagree. A
17
- fresh token is minted every `30m` rotation window and on every login or
18
- privilege elevation, with the previous token honored for one overlapping
19
- window to avoid breaking in-flight forms.
16
+ ## Properties
17
+
18
+ | Field | Type | Multiplicity | Constraints |
19
+ |---|---|---|---|
20
+ | token_id | UUID | 1..1 | identity |
21
+ | locator | String | 1..1 | minLength: 1 |
22
+ | bound_session_attribute | String | 1..1 | minLength: 1 |
23
+ | rotation_window | Duration | 1..1 | |
@@ -1,25 +1,30 @@
1
1
  ---
2
2
  id: CLASS-001
3
- title: "Confidential — tenant PII"
3
+ title: "DataRestricted"
4
4
  type: data_classification
5
+ object: data_classification
5
6
  ---
6
- <!-- data_classification authoring skeleton (spec-objects-security). Fill every
7
- part with substantive content. Contract (manifest body_extraction asserts):
8
- - Frontmatter MUST carry id, title, type (type:
9
- data_classification).
10
- - A "Handling" section MUST state the handling requirements for the class. -->
11
- # [CLASS-001] Confidential — tenant PII
12
-
13
- Classification applied to tenant member profile data (names, email addresses,
14
- phone numbers) and any document flagged as containing personal data. Sits one
15
- level below `Restricted` (secrets and key material) in the Atlas scheme.
7
+ <!-- data_classification authoring skeleton (spec-objects-security). Fill every section with
8
+ substantive content. Contract (manifest body_extraction asserts):
9
+ - Frontmatter MUST carry id, title, type: data_classification,
10
+ object: data_classification.
11
+ - "## Handling" (H2, required): how data of this class is handled.
12
+ - "## Properties" (H2): the typed declaration. DataClassification.json
13
+ requires a `level` row. -->
14
+ # [CLASS-001] DataRestricted
16
15
 
17
16
  ## Handling
18
17
 
19
- - Encrypted at rest with the tenant DEK (KEY-001) and in transit via TLS 1.3.
20
- - Access requires an explicit permission grant; reads are recorded in the
21
- audit stream as `pii.accessed` events.
22
- - Never written to application logs; log scrubbing rejects payloads matching
23
- the PII field allowlist.
24
- - Exported only through the GDPR export pipeline, with tenant-admin approval.
25
- - Retention: deleted within 30 days of tenant offboarding or subject request.
18
+ - Encrypted at rest with TenantDataEncryptionKey and in transit with TLS 1.3.
19
+ - Never written to application logs, traces, or error payloads.
20
+ - Exported only under ExportTenantData, and only to the owning tenant.
21
+ - Deleted within the retention period once the tenant contract ends.
22
+
23
+ ## Properties
24
+
25
+ | Field | Type | Multiplicity | Constraints |
26
+ |---|---|---|---|
27
+ | classification_id | UUID | 1..1 | identity |
28
+ | level | String | 1..1 | minLength: 1 |
29
+ | retention | Duration | 1..1 | |
30
+ | logging_permitted | Boolean | 1..1 | |
@@ -1,18 +1,25 @@
1
1
  ---
2
2
  id: KEY-001
3
- title: "Tenant data encryption key"
3
+ title: "TenantDataEncryptionKey"
4
4
  type: encryption_key
5
+ object: encryption_key
5
6
  algorithm: AES-256-GCM
6
- rotation: 90d
7
+ rotation: P90D
7
8
  ---
8
- <!-- encryption_key authoring skeleton (spec-objects-security). Fill every part
9
- with substantive content. Contract (manifest body_extraction asserts):
10
- - Frontmatter MUST carry id, title, type (type:
11
- encryption_key) and algorithm (the cipher); rotation gives the cadence. -->
12
- # [KEY-001] Tenant data encryption key
9
+ <!-- encryption_key authoring skeleton (spec-objects-security). Fill every section with
10
+ substantive content. Contract (manifest body_extraction asserts):
11
+ - Frontmatter MUST carry id, title, type: encryption_key,
12
+ object: encryption_key and algorithm; rotation is optional.
13
+ - "## Properties" (H2): the typed declaration. EncryptionKey.json admits
14
+ no field carrying a default, so key material is referenced, never held. -->
15
+ # [KEY-001] TenantDataEncryptionKey
13
16
 
14
- Per-tenant data encryption key (DEK) protecting documents at rest in the Atlas
15
- object store. Each DEK is wrapped by the platform KMS root key and unwrapped
16
- only inside the storage service; plaintext keys never leave process memory.
17
- Rotation re-wraps the DEK on the `90d` cadence declared above without
18
- re-encrypting historical objects, which remain readable via key versioning.
17
+ ## Properties
18
+
19
+ | Field | Type | Multiplicity | Constraints |
20
+ |---|---|---|---|
21
+ | key_id | UUID | 1..1 | identity |
22
+ | locator | String | 1..1 | minLength: 1 |
23
+ | lifecycle | String | 1..1 | minLength: 1 |
24
+ | key_length_bits | Integer | 1..1 | min: 256 |
25
+ | rotation_period | Duration | 1..1 | |
@@ -1,27 +1,38 @@
1
1
  ---
2
2
  id: CLAIM-001
3
- title: "tenant_id access-token claim"
3
+ title: "TenantIdentifierClaim"
4
4
  type: jwt_claim
5
+ object: jwt_claim
5
6
  ---
6
- <!-- jwt_claim authoring skeleton (spec-objects-security). Fill every part with
7
+ <!-- jwt_claim 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 (type:
9
- jwt_claim).
10
- - A "Schema" section MUST carry a ```json code block with the claim's
11
- JSON Schema. -->
12
- # [CLAIM-001] tenant_id access-token claim
9
+ - Frontmatter MUST carry id, title, type: jwt_claim, object: jwt_claim.
10
+ - "## Schema" (H2, required) holds a fenced `json` block. It is a
11
+ derived, human-facing view; the typed table is the authority.
12
+ - "## Properties" (H2): JwtClaim.json admits no field carrying a
13
+ default, so no claim value is written into the declaration. -->
14
+ # [CLAIM-001] TenantIdentifierClaim
13
15
 
14
- Custom claim stamped into every Atlas access token at issuance. The policy
15
- engine uses it for row-level tenant isolation (see PERM-001); tokens without a
16
- valid `tenant_id` are rejected at the gateway before reaching any service.
16
+ ## Properties
17
+
18
+ | Field | Type | Multiplicity | Constraints |
19
+ |---|---|---|---|
20
+ | claim_id | UUID | 1..1 | identity |
21
+ | claim_name | String | 1..1 | minLength: 1, maxLength: 32 |
22
+ | value_type | String | 1..1 | minLength: 1 |
23
+ | required_in_access_token | Boolean | 1..1 | |
17
24
 
18
25
  ## Schema
19
26
 
20
27
  ```json
21
28
  {
22
- "title": "tenant_id",
23
- "type": "string",
24
- "format": "uuid",
25
- "description": "UUID of the tenant the subject is acting within; immutable for the token lifetime"
29
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
30
+ "$id": "https://specs.agent-ix.dev/claims/tenant-identifier.schema.json",
31
+ "title": "TenantIdentifierClaim",
32
+ "type": "object",
33
+ "required": ["tid"],
34
+ "properties": {
35
+ "tid": { "type": "string", "format": "uuid" }
36
+ }
26
37
  }
27
38
  ```
@@ -1,19 +1,25 @@
1
1
  ---
2
2
  id: MFA-001
3
- title: "TOTP authenticator app"
3
+ title: "TimeBasedOneTimeCode"
4
4
  type: mfa_method
5
+ object: mfa_method
5
6
  kind: totp
6
7
  factor: possession
7
8
  ---
8
- <!-- mfa_method authoring skeleton (spec-objects-security). Fill every part with
9
+ <!-- mfa_method 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 (type:
11
- mfa_method) and kind (the method kind); factor names the auth factor
12
- class it provides. -->
13
- # [MFA-001] TOTP authenticator app
11
+ - Frontmatter MUST carry id, title, type: mfa_method, object: mfa_method
12
+ and kind; factor is optional.
13
+ - "## Properties" (H2): the typed declaration. MfaMethod.json admits no
14
+ field carrying a default, so no enrolment value can be embedded. -->
15
+ # [MFA-001] TimeBasedOneTimeCode
14
16
 
15
- Time-based one-time password second factor (RFC 6238) for Atlas operator and
16
- break-glass accounts. Secrets are 160-bit, provisioned via QR code over an
17
- authenticated session, and stored encrypted under KEY-001. Codes are 6 digits
18
- on a 30-second step with one step of clock skew accepted; ten single-use
19
- recovery codes are issued at enrollment for device-loss fallback.
17
+ ## Properties
18
+
19
+ | Field | Type | Multiplicity | Constraints |
20
+ |---|---|---|---|
21
+ | method_id | UUID | 1..1 | identity |
22
+ | factor_kind | String | 1..1 | minLength: 1 |
23
+ | code_digits | Integer | 1..1 | min: 6, max: 8 |
24
+ | step_period | Duration | 1..1 | |
25
+ | seed_locator | String | 1..1 | minLength: 1 |
@@ -1,26 +1,41 @@
1
1
  ---
2
- id: PASSPOL-001
3
- title: "Atlas local-account password policy"
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 part
7
- with substantive content. Contract (manifest body_extraction asserts):
8
- - Frontmatter MUST carry id, title, type (type:
9
- password_policy).
10
- - A "Rules" section MUST enumerate the enforced password rules. -->
11
- # [PASSPOL-001] Atlas local-account password policy
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
- - Minimum length 12 characters, maximum 128; all printable Unicode accepted
20
- - Candidate passwords are checked against the haveibeenpwned k-anonymity API
21
- and rejected when previously breached
22
- - No mandatory periodic rotation; rotation is forced only on suspected
23
- compromise
24
- - Hashing: argon2id with memory 64 MiB, iterations 3, parallelism 4
25
- - Rate limiting: 10 failed attempts per account per 15 minutes, then lockout
26
- with audit event emission
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
+ ```
@@ -1,18 +1,24 @@
1
1
  ---
2
2
  id: PERM-001
3
- title: "Read tenant documents"
3
+ title: "ExportTenantData"
4
4
  type: permission
5
- resource: document
6
- verb: read
5
+ object: permission
6
+ resource: "tenant/records"
7
+ verb: export
7
8
  ---
8
- <!-- permission authoring skeleton (spec-objects-security). Fill every part with
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 (type: permission).
11
- - Frontmatter MUST carry resource and verb (the protected resource kind and
12
- the action this permission grants on it). -->
13
- # [PERM-001] Read tenant documents
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
- Grants read access to `document` resources within the caller's tenant. Checked
16
- by the Atlas policy engine on every `GET /api/documents/*` request; the
17
- permission is satisfied only when the resource's `tenant_id` matches the
18
- caller's `tenant_id` claim.
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 | |