@agent-ix/spec-objects-security 0.4.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/LICENSE +661 -0
- package/README.md +109 -0
- package/manifest.yaml +464 -0
- package/package.json +33 -0
- package/skeletons/asset.md +22 -0
- package/skeletons/attack_surface.md +25 -0
- package/skeletons/audit_event.md +34 -0
- package/skeletons/audit_finding.md +25 -0
- package/skeletons/auth_flow.md +31 -0
- package/skeletons/control.md +24 -0
- package/skeletons/cors_policy.md +24 -0
- package/skeletons/csrf_token.md +19 -0
- package/skeletons/data_classification.md +25 -0
- package/skeletons/encryption_key.md +18 -0
- package/skeletons/jwt_claim.md +27 -0
- package/skeletons/mfa_method.md +19 -0
- package/skeletons/password_policy.md +26 -0
- package/skeletons/permission.md +18 -0
- package/skeletons/policy.md +25 -0
- package/skeletons/risk.md +20 -0
- package/skeletons/role.md +21 -0
- package/skeletons/scope.md +20 -0
- package/skeletons/secret.md +25 -0
- package/skeletons/session_config.md +24 -0
- package/skeletons/threat.md +19 -0
- package/skeletons/trust_boundary.md +26 -0
- package/skeletons/vulnerability.md +21 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: FIND-001
|
|
3
|
+
title: "Session store reachable with eval privileges"
|
|
4
|
+
type: audit_finding
|
|
5
|
+
---
|
|
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.
|
|
17
|
+
|
|
18
|
+
## Recommendation
|
|
19
|
+
|
|
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.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: AUTHFLOW-001
|
|
3
|
+
title: "Tenant user login via OIDC authorization code flow"
|
|
4
|
+
type: auth_flow
|
|
5
|
+
---
|
|
6
|
+
<!-- auth_flow authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
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.
|
|
15
|
+
|
|
16
|
+
## Flow
|
|
17
|
+
|
|
18
|
+
```mermaid
|
|
19
|
+
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
|
|
31
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: CTRL-001
|
|
3
|
+
title: "Refresh token rotation with reuse detection"
|
|
4
|
+
type: control
|
|
5
|
+
---
|
|
6
|
+
<!-- control authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
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.
|
|
17
|
+
|
|
18
|
+
## Mappings
|
|
19
|
+
|
|
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)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: CORS-001
|
|
3
|
+
title: "Atlas public API CORS policy"
|
|
4
|
+
type: cors_policy
|
|
5
|
+
---
|
|
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.
|
|
16
|
+
|
|
17
|
+
## Origins
|
|
18
|
+
|
|
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`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: CSRF-001
|
|
3
|
+
title: "Atlas web CSRF token"
|
|
4
|
+
type: csrf_token
|
|
5
|
+
rotation_window: 30m
|
|
6
|
+
---
|
|
7
|
+
<!-- csrf_token authoring skeleton (spec-objects-security). Fill every part with
|
|
8
|
+
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
|
|
12
|
+
|
|
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.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: CLASS-001
|
|
3
|
+
title: "Confidential — tenant PII"
|
|
4
|
+
type: data_classification
|
|
5
|
+
---
|
|
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.
|
|
16
|
+
|
|
17
|
+
## Handling
|
|
18
|
+
|
|
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.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: KEY-001
|
|
3
|
+
title: "Tenant data encryption key"
|
|
4
|
+
type: encryption_key
|
|
5
|
+
algorithm: AES-256-GCM
|
|
6
|
+
rotation: 90d
|
|
7
|
+
---
|
|
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
|
|
13
|
+
|
|
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.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: CLAIM-001
|
|
3
|
+
title: "tenant_id access-token claim"
|
|
4
|
+
type: jwt_claim
|
|
5
|
+
---
|
|
6
|
+
<!-- jwt_claim authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
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
|
|
13
|
+
|
|
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.
|
|
17
|
+
|
|
18
|
+
## Schema
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
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"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: MFA-001
|
|
3
|
+
title: "TOTP authenticator app"
|
|
4
|
+
type: mfa_method
|
|
5
|
+
kind: totp
|
|
6
|
+
factor: possession
|
|
7
|
+
---
|
|
8
|
+
<!-- mfa_method authoring skeleton (spec-objects-security). Fill every part with
|
|
9
|
+
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
|
|
14
|
+
|
|
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.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: PASSPOL-001
|
|
3
|
+
title: "Atlas local-account password policy"
|
|
4
|
+
type: password_policy
|
|
5
|
+
---
|
|
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.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
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
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: PERM-001
|
|
3
|
+
title: "Read tenant documents"
|
|
4
|
+
type: permission
|
|
5
|
+
resource: document
|
|
6
|
+
verb: read
|
|
7
|
+
---
|
|
8
|
+
<!-- permission authoring skeleton (spec-objects-security). Fill every part with
|
|
9
|
+
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
|
|
14
|
+
|
|
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.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: POL-001
|
|
3
|
+
title: "Access token lifetime policy"
|
|
4
|
+
type: policy
|
|
5
|
+
---
|
|
6
|
+
<!-- policy authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
+
- Frontmatter MUST carry id, title, type (type: policy).
|
|
9
|
+
- A "Policy" section MUST state the normative policy text. -->
|
|
10
|
+
# [POL-001] Access token lifetime policy
|
|
11
|
+
|
|
12
|
+
Platform-wide policy governing bearer token lifetimes for Atlas first-party
|
|
13
|
+
and integration clients, owned by the security council.
|
|
14
|
+
|
|
15
|
+
## Policy
|
|
16
|
+
|
|
17
|
+
- Access tokens SHALL expire no later than 15 minutes after issuance.
|
|
18
|
+
- Refresh tokens SHALL be single-use and rotated on every exchange (CTRL-001),
|
|
19
|
+
with an absolute family lifetime of 30 days.
|
|
20
|
+
- Integration tokens issued for SCOPE-001 SHALL be bound to the requesting
|
|
21
|
+
client via DPoP or mTLS sender constraint.
|
|
22
|
+
- Token lifetimes SHALL NOT be extended per tenant without a documented risk
|
|
23
|
+
acceptance referencing the affected risk register entries (e.g. RISK-001).
|
|
24
|
+
- Violations surface as audit findings (FIND-001 family) and block release
|
|
25
|
+
until remediated or formally accepted.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: RISK-001
|
|
3
|
+
title: "Tenant account takeover via token theft"
|
|
4
|
+
type: risk
|
|
5
|
+
likelihood: medium
|
|
6
|
+
impact: high
|
|
7
|
+
---
|
|
8
|
+
<!-- risk authoring skeleton (spec-objects-security). Fill every part with
|
|
9
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
10
|
+
- Frontmatter MUST carry id, title, type (type: risk),
|
|
11
|
+
likelihood and impact (qualitative ratings driving the risk score). -->
|
|
12
|
+
# [RISK-001] Tenant account takeover via token theft
|
|
13
|
+
|
|
14
|
+
Business risk that an attacker realizes THREAT-001 and operates inside a
|
|
15
|
+
tenant with the victim's permissions, exposing Confidential PII (CLASS-001)
|
|
16
|
+
and enabling data exfiltration through the public API. Likelihood is rated
|
|
17
|
+
medium given commodity infostealer malware; impact is high because a single
|
|
18
|
+
takeover crosses into regulated personal data. Primary treatment is CTRL-001;
|
|
19
|
+
residual risk after rotation and reuse detection is accepted at low/high and
|
|
20
|
+
reviewed quarterly by the security council.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ROLE-001
|
|
3
|
+
title: "Tenant Auditor role"
|
|
4
|
+
type: role
|
|
5
|
+
---
|
|
6
|
+
<!-- role authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
+
- Frontmatter MUST carry id, title, type (type: role).
|
|
9
|
+
- A "Permissions" section MUST list the permissions bound to the role. -->
|
|
10
|
+
# [ROLE-001] Tenant Auditor role
|
|
11
|
+
|
|
12
|
+
Read-only compliance role assigned to external auditors invited into a tenant.
|
|
13
|
+
Members can inspect configuration and audit trails but can never mutate state
|
|
14
|
+
or read secret material.
|
|
15
|
+
|
|
16
|
+
## Permissions
|
|
17
|
+
|
|
18
|
+
- PERM-001 — `document:read` within the tenant
|
|
19
|
+
- `audit_log:read` — query the tenant's audit event stream
|
|
20
|
+
- `policy:read` — view security policies in effect for the tenant
|
|
21
|
+
- `member:list` — enumerate tenant members and their role bindings
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SCOPE-001
|
|
3
|
+
title: "documents:read OAuth scope"
|
|
4
|
+
type: scope
|
|
5
|
+
---
|
|
6
|
+
<!-- scope authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
+
- Frontmatter MUST carry id, title, type (type: scope).
|
|
9
|
+
- A "Grants" section MUST list the permissions this scope confers. -->
|
|
10
|
+
# [SCOPE-001] documents:read OAuth scope
|
|
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.
|
|
14
|
+
|
|
15
|
+
## Grants
|
|
16
|
+
|
|
17
|
+
- PERM-001 — read tenant documents (`document:read`)
|
|
18
|
+
- Listing document metadata via `GET /api/documents`
|
|
19
|
+
- Downloading rendered document content via `GET /api/documents/{id}/content`
|
|
20
|
+
- Excludes all write verbs; mutations require the separate `documents:write` scope
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SECRET-001
|
|
3
|
+
title: "Atlas OIDC client secret"
|
|
4
|
+
type: secret
|
|
5
|
+
---
|
|
6
|
+
<!-- secret authoring skeleton (spec-objects-security). Fill every part with
|
|
7
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
8
|
+
- Frontmatter MUST carry id, title, type (type: secret).
|
|
9
|
+
- A "Rotation" section MUST describe how and how often the secret rotates. -->
|
|
10
|
+
# [SECRET-001] Atlas OIDC client secret
|
|
11
|
+
|
|
12
|
+
Confidential client secret used by the Atlas backend to authenticate at the
|
|
13
|
+
tenant IdP token endpoint during the authorization code exchange. Stored in
|
|
14
|
+
Vault at `secret/atlas/oidc/client-secret`; never present in environment files
|
|
15
|
+
or images.
|
|
16
|
+
|
|
17
|
+
## Rotation
|
|
18
|
+
|
|
19
|
+
- Rotated every 90 days by the `secret-rotator` job, which registers a new
|
|
20
|
+
secret with the IdP before revoking the old one.
|
|
21
|
+
- Dual-secret overlap window of 24 hours so in-flight token exchanges complete
|
|
22
|
+
against either value.
|
|
23
|
+
- Emergency rotation is triggered manually via `atlasctl secrets rotate
|
|
24
|
+
oidc-client` and completes in under 5 minutes.
|
|
25
|
+
- Every rotation emits an `secret.rotated` audit event (AUDIT-001 family).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SESSION-001
|
|
3
|
+
title: "Atlas web session configuration"
|
|
4
|
+
type: session_config
|
|
5
|
+
---
|
|
6
|
+
<!-- session_config 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
|
+
session_config).
|
|
10
|
+
- A "Settings" section MUST enumerate the effective session parameters. -->
|
|
11
|
+
# [SESSION-001] Atlas web session configuration
|
|
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.
|
|
16
|
+
|
|
17
|
+
## Settings
|
|
18
|
+
|
|
19
|
+
- Cookie name: `__Host-atlas_session` (Secure, HttpOnly, SameSite=Lax, Path=/)
|
|
20
|
+
- Idle timeout: 30 minutes, sliding on authenticated requests
|
|
21
|
+
- Absolute lifetime: 12 hours, after which re-authentication is required
|
|
22
|
+
- Session identifier: 256-bit random value, regenerated on login and on
|
|
23
|
+
privilege elevation
|
|
24
|
+
- Concurrent session cap: 5 per user; the oldest session is evicted on overflow
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: THREAT-001
|
|
3
|
+
title: "Stolen refresh token replay"
|
|
4
|
+
type: threat
|
|
5
|
+
stride_category: Spoofing
|
|
6
|
+
vector: "exfiltrated refresh token replayed from attacker infrastructure"
|
|
7
|
+
---
|
|
8
|
+
<!-- threat authoring skeleton (spec-objects-security). Fill every part with
|
|
9
|
+
substantive content. Contract (manifest body_extraction asserts):
|
|
10
|
+
- Frontmatter MUST carry id, title, type (type: threat),
|
|
11
|
+
stride_category (STRIDE class) and vector (the attack vector). -->
|
|
12
|
+
# [THREAT-001] Stolen refresh token replay
|
|
13
|
+
|
|
14
|
+
An attacker who exfiltrates a refresh token (malware on the user device, log
|
|
15
|
+
leakage, or proxy compromise) replays it against the token endpoint to mint
|
|
16
|
+
fresh access tokens and impersonate the user across tenant APIs. Detection is
|
|
17
|
+
hard because requests are well-formed; mitigation relies on refresh token
|
|
18
|
+
rotation with reuse detection (CTRL-001) and the trust boundary controls in
|
|
19
|
+
BOUND-001. Realized risk is tracked as RISK-001.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: BOUND-001
|
|
3
|
+
title: "Edge to auth-service trust boundary"
|
|
4
|
+
type: trust_boundary
|
|
5
|
+
---
|
|
6
|
+
<!-- trust_boundary 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
|
+
trust_boundary).
|
|
10
|
+
- A "Boundary" section MUST carry a ```mermaid code block drawing the
|
|
11
|
+
boundary between zones. -->
|
|
12
|
+
# [BOUND-001] Edge to auth-service trust boundary
|
|
13
|
+
|
|
14
|
+
Separates the untrusted internet, the DMZ-hosted API gateway, and the trusted
|
|
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.
|
|
18
|
+
|
|
19
|
+
## Boundary
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
flowchart LR
|
|
23
|
+
Browser["Tenant browser (untrusted)"] -->|HTTPS| Gateway["API gateway (DMZ)"]
|
|
24
|
+
Gateway -->|mTLS| AuthSvc["Auth service (trusted subnet)"]
|
|
25
|
+
AuthSvc -->|private VPC peering| SessionDB[("Session store")]
|
|
26
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: VULN-001
|
|
3
|
+
title: "Session store affected by lua sandbox escape"
|
|
4
|
+
type: vulnerability
|
|
5
|
+
cve_id: CVE-2024-31449
|
|
6
|
+
severity: high
|
|
7
|
+
---
|
|
8
|
+
<!-- vulnerability 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
|
+
vulnerability) and severity; cve_id links the public advisory when one
|
|
12
|
+
exists. -->
|
|
13
|
+
# [VULN-001] Session store affected by lua sandbox escape
|
|
14
|
+
|
|
15
|
+
The Redis instance backing the Atlas session store (ASSET-001) runs a version
|
|
16
|
+
affected by CVE-2024-31449, where an authenticated client can craft a Lua
|
|
17
|
+
script that escapes the sandbox and executes arbitrary code. Exploitation
|
|
18
|
+
requires script-eval privileges inside the private subnet, so exposure is
|
|
19
|
+
limited by BOUND-001, but a compromised service credential would yield full
|
|
20
|
+
session-store control. Remediation: upgrade to a patched Redis release and
|
|
21
|
+
disable `EVAL` for application credentials; tracked to closure in FIND-001.
|