@superblocksteam/shared 0.9586.7 → 0.9589.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/dist/database-lifecycle/index.d.ts +115 -0
- package/dist/database-lifecycle/index.d.ts.map +1 -0
- package/dist/database-lifecycle/index.js +143 -0
- package/dist/database-lifecycle/index.js.map +1 -0
- package/dist/database-lifecycle/index.test.d.ts +2 -0
- package/dist/database-lifecycle/index.test.d.ts.map +1 -0
- package/dist/database-lifecycle/index.test.js +110 -0
- package/dist/database-lifecycle/index.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +4 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/templates/dropbox.d.ts.map +1 -1
- package/dist/plugins/templates/dropbox.js +5 -1
- package/dist/plugins/templates/dropbox.js.map +1 -1
- package/dist/plugins/templates/shared/auth.d.ts +2 -0
- package/dist/plugins/templates/shared/auth.d.ts.map +1 -1
- package/dist/plugins/templates/shared/auth.js +1 -0
- package/dist/plugins/templates/shared/auth.js.map +1 -1
- package/dist/types/ai/index.d.ts +1 -0
- package/dist/types/ai/index.d.ts.map +1 -1
- package/dist/types/ai/index.js +1 -0
- package/dist/types/ai/index.js.map +1 -1
- package/dist/types/ai/provider-credential.d.ts +53 -0
- package/dist/types/ai/provider-credential.d.ts.map +1 -0
- package/dist/types/ai/provider-credential.js +10 -0
- package/dist/types/ai/provider-credential.js.map +1 -0
- package/dist/types/audit/ocsf.d.ts.map +1 -1
- package/dist/types/audit/ocsf.js +8 -2
- package/dist/types/audit/ocsf.js.map +1 -1
- package/dist/types/commit/index.d.ts +1 -0
- package/dist/types/commit/index.d.ts.map +1 -1
- package/dist/types/commit/index.js.map +1 -1
- package/dist/types/common/env.d.ts +25 -0
- package/dist/types/common/env.d.ts.map +1 -1
- package/dist/types/common/env.js +29 -1
- package/dist/types/common/env.js.map +1 -1
- package/dist/types/common/env.test.d.ts +2 -0
- package/dist/types/common/env.test.d.ts.map +1 -0
- package/dist/types/common/env.test.js +24 -0
- package/dist/types/common/env.test.js.map +1 -0
- package/dist/types/common/response.d.ts +2 -2
- package/dist/types/common/response.d.ts.map +1 -1
- package/dist/types/common/response.js.map +1 -1
- package/dist/types/credentials/index.d.ts +7 -7
- package/dist/types/credentials/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/integration/index.d.ts +5 -2
- package/dist/types/integration/index.d.ts.map +1 -1
- package/dist/types/integration/index.js +5 -2
- package/dist/types/integration/index.js.map +1 -1
- package/dist/types/organization/agent.js +10 -40
- package/dist/types/organization/agent.js.map +1 -1
- package/dist/types/plugin/form.d.ts +1 -0
- package/dist/types/plugin/form.d.ts.map +1 -1
- package/dist/types/policyGate/index.d.ts +32 -0
- package/dist/types/policyGate/index.d.ts.map +1 -0
- package/dist/types/policyGate/index.js +3 -0
- package/dist/types/policyGate/index.js.map +1 -0
- package/dist/types/rbac/index.d.ts +8 -0
- package/dist/types/rbac/index.d.ts.map +1 -1
- package/dist/types/rbac/index.js +5 -0
- package/dist/types/rbac/index.js.map +1 -1
- package/dist/types/response/GetPolicyGateReadinessResponseBody.d.ts +3 -0
- package/dist/types/response/GetPolicyGateReadinessResponseBody.d.ts.map +1 -0
- package/dist/types/response/GetPolicyGateReadinessResponseBody.js +3 -0
- package/dist/types/response/GetPolicyGateReadinessResponseBody.js.map +1 -0
- package/dist/types/response/index.d.ts +1 -0
- package/dist/types/response/index.d.ts.map +1 -1
- package/dist/types/response/index.js +1 -0
- package/dist/types/response/index.js.map +1 -1
- package/dist/types/reviewPolicy/index.d.ts +105 -0
- package/dist/types/reviewPolicy/index.d.ts.map +1 -0
- package/dist/types/reviewPolicy/index.js +47 -0
- package/dist/types/reviewPolicy/index.js.map +1 -0
- package/dist/utils/git-url.d.ts +0 -1
- package/dist/utils/git-url.d.ts.map +1 -1
- package/dist/utils/git-url.js +1 -2
- package/dist/utils/git-url.js.map +1 -1
- package/dist/utils/git-url.test.js +0 -5
- package/dist/utils/git-url.test.js.map +1 -1
- package/dist-esm/database-lifecycle/index.d.ts +115 -0
- package/dist-esm/database-lifecycle/index.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/index.js +131 -0
- package/dist-esm/database-lifecycle/index.js.map +1 -0
- package/dist-esm/database-lifecycle/index.test.d.ts +2 -0
- package/dist-esm/database-lifecycle/index.test.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/index.test.js +108 -0
- package/dist-esm/database-lifecycle/index.test.js.map +1 -0
- package/dist-esm/index.d.ts +1 -0
- package/dist-esm/index.d.ts.map +1 -1
- package/dist-esm/index.js +1 -0
- package/dist-esm/index.js.map +1 -1
- package/dist-esm/plugins/index.d.ts.map +1 -1
- package/dist-esm/plugins/index.js +4 -0
- package/dist-esm/plugins/index.js.map +1 -1
- package/dist-esm/plugins/templates/dropbox.d.ts.map +1 -1
- package/dist-esm/plugins/templates/dropbox.js +5 -1
- package/dist-esm/plugins/templates/dropbox.js.map +1 -1
- package/dist-esm/plugins/templates/shared/auth.d.ts +2 -0
- package/dist-esm/plugins/templates/shared/auth.d.ts.map +1 -1
- package/dist-esm/plugins/templates/shared/auth.js +1 -0
- package/dist-esm/plugins/templates/shared/auth.js.map +1 -1
- package/dist-esm/types/ai/index.d.ts +1 -0
- package/dist-esm/types/ai/index.d.ts.map +1 -1
- package/dist-esm/types/ai/index.js +1 -0
- package/dist-esm/types/ai/index.js.map +1 -1
- package/dist-esm/types/ai/provider-credential.d.ts +53 -0
- package/dist-esm/types/ai/provider-credential.d.ts.map +1 -0
- package/dist-esm/types/ai/provider-credential.js +7 -0
- package/dist-esm/types/ai/provider-credential.js.map +1 -0
- package/dist-esm/types/audit/ocsf.d.ts.map +1 -1
- package/dist-esm/types/audit/ocsf.js +8 -2
- package/dist-esm/types/audit/ocsf.js.map +1 -1
- package/dist-esm/types/commit/index.d.ts +1 -0
- package/dist-esm/types/commit/index.d.ts.map +1 -1
- package/dist-esm/types/commit/index.js.map +1 -1
- package/dist-esm/types/common/env.d.ts +25 -0
- package/dist-esm/types/common/env.d.ts.map +1 -1
- package/dist-esm/types/common/env.js +27 -0
- package/dist-esm/types/common/env.js.map +1 -1
- package/dist-esm/types/common/env.test.d.ts +2 -0
- package/dist-esm/types/common/env.test.d.ts.map +1 -0
- package/dist-esm/types/common/env.test.js +22 -0
- package/dist-esm/types/common/env.test.js.map +1 -0
- package/dist-esm/types/common/response.d.ts +2 -2
- package/dist-esm/types/common/response.d.ts.map +1 -1
- package/dist-esm/types/common/response.js.map +1 -1
- package/dist-esm/types/credentials/index.d.ts +7 -7
- package/dist-esm/types/credentials/index.d.ts.map +1 -1
- package/dist-esm/types/index.d.ts +2 -0
- package/dist-esm/types/index.d.ts.map +1 -1
- package/dist-esm/types/index.js +2 -0
- package/dist-esm/types/index.js.map +1 -1
- package/dist-esm/types/integration/index.d.ts +5 -2
- package/dist-esm/types/integration/index.d.ts.map +1 -1
- package/dist-esm/types/integration/index.js +5 -2
- package/dist-esm/types/integration/index.js.map +1 -1
- package/dist-esm/types/organization/agent.js +1 -1
- package/dist-esm/types/organization/agent.js.map +1 -1
- package/dist-esm/types/plugin/form.d.ts +1 -0
- package/dist-esm/types/plugin/form.d.ts.map +1 -1
- package/dist-esm/types/policyGate/index.d.ts +32 -0
- package/dist-esm/types/policyGate/index.d.ts.map +1 -0
- package/dist-esm/types/policyGate/index.js +2 -0
- package/dist-esm/types/policyGate/index.js.map +1 -0
- package/dist-esm/types/rbac/index.d.ts +8 -0
- package/dist-esm/types/rbac/index.d.ts.map +1 -1
- package/dist-esm/types/rbac/index.js +5 -0
- package/dist-esm/types/rbac/index.js.map +1 -1
- package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.d.ts +3 -0
- package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.d.ts.map +1 -0
- package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.js +2 -0
- package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.js.map +1 -0
- package/dist-esm/types/response/index.d.ts +1 -0
- package/dist-esm/types/response/index.d.ts.map +1 -1
- package/dist-esm/types/response/index.js +1 -0
- package/dist-esm/types/response/index.js.map +1 -1
- package/dist-esm/types/reviewPolicy/index.d.ts +105 -0
- package/dist-esm/types/reviewPolicy/index.d.ts.map +1 -0
- package/dist-esm/types/reviewPolicy/index.js +44 -0
- package/dist-esm/types/reviewPolicy/index.js.map +1 -0
- package/dist-esm/utils/git-url.d.ts +0 -1
- package/dist-esm/utils/git-url.d.ts.map +1 -1
- package/dist-esm/utils/git-url.js +0 -1
- package/dist-esm/utils/git-url.js.map +1 -1
- package/dist-esm/utils/git-url.test.js +1 -6
- package/dist-esm/utils/git-url.test.js.map +1 -1
- package/package.json +3 -3
- package/src/database-lifecycle/index.test.ts +132 -0
- package/src/database-lifecycle/index.ts +278 -0
- package/src/index.ts +1 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/templates/dropbox.ts +5 -1
- package/src/plugins/templates/shared/auth.ts +3 -0
- package/src/types/ai/index.ts +1 -0
- package/src/types/ai/provider-credential.ts +61 -0
- package/src/types/audit/ocsf.ts +8 -2
- package/src/types/commit/index.ts +1 -0
- package/src/types/common/env.test.ts +27 -0
- package/src/types/common/env.ts +28 -0
- package/src/types/common/response.ts +2 -2
- package/src/types/credentials/index.ts +7 -7
- package/src/types/index.ts +2 -0
- package/src/types/integration/index.ts +9 -2
- package/src/types/organization/agent.ts +1 -1
- package/src/types/plugin/form.ts +1 -0
- package/src/types/policyGate/index.ts +52 -0
- package/src/types/rbac/index.ts +6 -0
- package/src/types/response/GetPolicyGateReadinessResponseBody.ts +3 -0
- package/src/types/response/index.ts +1 -0
- package/src/types/reviewPolicy/index.ts +132 -0
- package/src/utils/git-url.test.ts +1 -14
- package/src/utils/git-url.ts +0 -2
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { sha256Base64 } from '../signing/hashing.js';
|
|
2
|
+
|
|
3
|
+
export const LIFECYCLE_TERMINAL_STATES = ['ready', 'failed', 'cancelled'] as const;
|
|
4
|
+
export const LIFECYCLE_NON_TERMINAL_STATES = ['pending', 'provisioning', 'migrating', 'retiring'] as const;
|
|
5
|
+
export const LIFECYCLE_STATES = [...LIFECYCLE_NON_TERMINAL_STATES, ...LIFECYCLE_TERMINAL_STATES] as const;
|
|
6
|
+
export const LIFECYCLE_MIGRATION_STATES = ['pending', 'migrated', 'failed'] as const;
|
|
7
|
+
export const LIFECYCLE_OPERATIONS = ['ensure_dev_database', 'ensure_prod_database', 'migrate_schema', 'retire_database'] as const;
|
|
8
|
+
export const ENVIRONMENT_CLASSES = ['dev', 'staging', 'prod'] as const;
|
|
9
|
+
export const DATABASE_ENGINES = ['postgres', 'snowflake', 'snowflake_postgres', 'lakebase'] as const;
|
|
10
|
+
export const DATABASE_LIFECYCLE_MANAGED_BY = 'database_lifecycle';
|
|
11
|
+
|
|
12
|
+
export type EnvironmentClass = (typeof ENVIRONMENT_CLASSES)[number];
|
|
13
|
+
export type DatabaseEngine = (typeof DATABASE_ENGINES)[number];
|
|
14
|
+
export type LifecycleOperation = (typeof LIFECYCLE_OPERATIONS)[number];
|
|
15
|
+
export type LifecycleTerminalState = (typeof LIFECYCLE_TERMINAL_STATES)[number];
|
|
16
|
+
export type LifecycleNonTerminalState = (typeof LIFECYCLE_NON_TERMINAL_STATES)[number];
|
|
17
|
+
export type LifecycleState = (typeof LIFECYCLE_STATES)[number];
|
|
18
|
+
export type LifecycleMigrationState = (typeof LIFECYCLE_MIGRATION_STATES)[number];
|
|
19
|
+
|
|
20
|
+
export type LifecycleErrorCode =
|
|
21
|
+
| 'unsupported_provider_capability'
|
|
22
|
+
| 'backend_locked'
|
|
23
|
+
| 'policy_blocked'
|
|
24
|
+
| 'credential_resolution_failed'
|
|
25
|
+
| 'terraform_failed'
|
|
26
|
+
| 'callback_failed';
|
|
27
|
+
|
|
28
|
+
export const CREDENTIAL_RESOLVERS = ['opa_local', 'aws_secrets_manager', 'gcp_secret_manager', 'vault', 'kubernetes_secret'] as const;
|
|
29
|
+
export type CredentialResolver = (typeof CREDENTIAL_RESOLVERS)[number];
|
|
30
|
+
|
|
31
|
+
export type CredentialRef = {
|
|
32
|
+
resolver: CredentialResolver;
|
|
33
|
+
ref: string;
|
|
34
|
+
field?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type DatabaseRequirement = {
|
|
38
|
+
logicalName: string;
|
|
39
|
+
engine: DatabaseEngine;
|
|
40
|
+
version?: string;
|
|
41
|
+
sizing?: Record<string, unknown>;
|
|
42
|
+
extensions?: readonly string[];
|
|
43
|
+
replicaCount?: number;
|
|
44
|
+
migrationDirectory?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type TerraformDatabaseBackend = {
|
|
48
|
+
provisioner: 'terraform';
|
|
49
|
+
provider: 'aws-rds' | 'snowflake' | 'databricks';
|
|
50
|
+
stateBackend: 's3' | 'gcs' | 'azurerm' | 'local';
|
|
51
|
+
remoteState: boolean;
|
|
52
|
+
locking: boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type DatabaseBackend = TerraformDatabaseBackend;
|
|
56
|
+
|
|
57
|
+
export type EnvironmentProfile = {
|
|
58
|
+
id: string;
|
|
59
|
+
organizationId: string;
|
|
60
|
+
environmentClass: EnvironmentClass;
|
|
61
|
+
environmentName: string;
|
|
62
|
+
opaAgentId: string;
|
|
63
|
+
supportedOperations: LifecycleOperation[];
|
|
64
|
+
supportedEngines: DatabaseEngine[];
|
|
65
|
+
backend: DatabaseBackend;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type DatabaseBindingBase = {
|
|
69
|
+
id: string;
|
|
70
|
+
bindingKey: string;
|
|
71
|
+
requirementKey: string;
|
|
72
|
+
logicalName: string;
|
|
73
|
+
applicationId: string;
|
|
74
|
+
environmentClass: EnvironmentClass;
|
|
75
|
+
environmentName: string;
|
|
76
|
+
desiredSpecHash: string;
|
|
77
|
+
migrationState: LifecycleMigrationState;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// A binding that has completed provisioning. The `ready` discriminant
|
|
81
|
+
// guarantees that the OPA returned both `connectionMetadata` and
|
|
82
|
+
// `credentialRefs`, so consumers can rely on those without null checks.
|
|
83
|
+
export type ReadyDatabaseBinding = DatabaseBindingBase & {
|
|
84
|
+
lifecycleState: 'ready';
|
|
85
|
+
connectionMetadata: Record<string, string | number | boolean>;
|
|
86
|
+
credentialRefs: Record<string, CredentialRef>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// A binding in any non-ready state. Connection metadata and credential refs
|
|
90
|
+
// may be present from a prior `ready` transition or absent if the binding
|
|
91
|
+
// never reached `ready`.
|
|
92
|
+
export type IncompleteDatabaseBinding = DatabaseBindingBase & {
|
|
93
|
+
lifecycleState: Exclude<LifecycleState, 'ready'>;
|
|
94
|
+
connectionMetadata?: Record<string, string | number | boolean>;
|
|
95
|
+
credentialRefs?: Record<string, CredentialRef>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export type DatabaseBinding = ReadyDatabaseBinding | IncompleteDatabaseBinding;
|
|
99
|
+
|
|
100
|
+
export function isReadyDatabaseBinding(binding: DatabaseBinding): binding is ReadyDatabaseBinding {
|
|
101
|
+
return binding.lifecycleState === 'ready';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type LifecycleRequest = {
|
|
105
|
+
id: string;
|
|
106
|
+
operation: LifecycleOperation;
|
|
107
|
+
bindingKey: string;
|
|
108
|
+
requirementKey: string;
|
|
109
|
+
desiredSpecHash: string;
|
|
110
|
+
state: LifecycleState;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export type TerraformModuleInput = {
|
|
114
|
+
bindingKey: string;
|
|
115
|
+
requirement: DatabaseRequirement;
|
|
116
|
+
credentialRefs: Record<string, CredentialRef>;
|
|
117
|
+
metadata: Record<string, string>;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type TerraformModuleOutput = {
|
|
121
|
+
connection: Record<string, string | number | boolean>;
|
|
122
|
+
credentialRefs: Record<string, CredentialRef>;
|
|
123
|
+
resourceKey: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export function computeRequirementKey(requirement: Pick<DatabaseRequirement, 'logicalName' | 'engine'>): string {
|
|
127
|
+
return `${slugify(requirement.logicalName)}~${encodeURIComponent(requirement.logicalName)}:${requirement.engine}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function computeBindingKey(input: {
|
|
131
|
+
organizationId: string;
|
|
132
|
+
applicationId: string;
|
|
133
|
+
environmentClass: EnvironmentClass;
|
|
134
|
+
environmentName: string;
|
|
135
|
+
requirementKey: string;
|
|
136
|
+
}): string {
|
|
137
|
+
return [input.organizationId, ...bindingKeySegments(input)].join(':');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function computeLegacyBindingKeyWithoutOrganization(input: {
|
|
141
|
+
applicationId: string;
|
|
142
|
+
environmentClass: EnvironmentClass;
|
|
143
|
+
environmentName: string;
|
|
144
|
+
requirementKey: string;
|
|
145
|
+
}): string {
|
|
146
|
+
return bindingKeySegments(input).join(':');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function bindingKeySegments(input: {
|
|
150
|
+
applicationId: string;
|
|
151
|
+
environmentClass: EnvironmentClass;
|
|
152
|
+
environmentName: string;
|
|
153
|
+
requirementKey: string;
|
|
154
|
+
}): string[] {
|
|
155
|
+
return [
|
|
156
|
+
input.applicationId,
|
|
157
|
+
input.environmentClass,
|
|
158
|
+
`${slugify(input.environmentName)}~${encodeURIComponent(input.environmentName)}`,
|
|
159
|
+
input.requirementKey
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function computeDesiredSpecHash(requirement: DatabaseRequirement): Promise<string> {
|
|
164
|
+
return await sha256Base64(JSON.stringify(canonicalize(requirement)));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function isTerminalLifecycleState(state: LifecycleState): state is LifecycleTerminalState {
|
|
168
|
+
return (LIFECYCLE_TERMINAL_STATES as readonly string[]).includes(state);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function redactLifecycleSecrets<T>(value: T): T {
|
|
172
|
+
return redact(value) as T;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function slugify(value: string): string {
|
|
176
|
+
return value
|
|
177
|
+
.trim()
|
|
178
|
+
.toLowerCase()
|
|
179
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
180
|
+
.replace(/^-|-$/g, '');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function canonicalize(value: unknown): unknown {
|
|
184
|
+
if (Array.isArray(value)) {
|
|
185
|
+
return value.map(canonicalize).sort((left, right) => codepointCompare(JSON.stringify(left), JSON.stringify(right)));
|
|
186
|
+
}
|
|
187
|
+
if (value && typeof value === 'object') {
|
|
188
|
+
return Object.fromEntries(
|
|
189
|
+
Object.entries(value)
|
|
190
|
+
.filter(([, entryValue]) => entryValue !== undefined)
|
|
191
|
+
.sort(([left], [right]) => codepointCompare(left, right))
|
|
192
|
+
.map(([key, entryValue]) => [key, canonicalize(entryValue)])
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function codepointCompare(left: string, right: string): number {
|
|
199
|
+
if (left < right) {
|
|
200
|
+
return -1;
|
|
201
|
+
}
|
|
202
|
+
if (left > right) {
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function redact(value: unknown, preserveCredentialRefs = false): unknown {
|
|
209
|
+
if (Array.isArray(value)) {
|
|
210
|
+
return value.map((entry) => redact(entry, preserveCredentialRefs));
|
|
211
|
+
}
|
|
212
|
+
if (value && typeof value === 'object') {
|
|
213
|
+
return Object.fromEntries(
|
|
214
|
+
Object.entries(value).map(([key, entryValue]) => {
|
|
215
|
+
if (isCredentialRefKey(key)) {
|
|
216
|
+
return [key, redact(entryValue, true)];
|
|
217
|
+
}
|
|
218
|
+
if (!preserveCredentialRefs && SECRET_KEY_PATTERN.test(key)) {
|
|
219
|
+
return [key, '[REDACTED]'];
|
|
220
|
+
}
|
|
221
|
+
return [key, redact(entryValue, preserveCredentialRefs)];
|
|
222
|
+
})
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Shared secret-key tripwire: keys whose name strongly suggests a raw secret
|
|
229
|
+
// value. Used by `redactLifecycleSecrets` to scrub logs/error reporting and by
|
|
230
|
+
// the server-side `assertNoRawCredentialMaterial` guard to refuse callbacks
|
|
231
|
+
// that violate the typed-CredentialRef contract. NOT a security boundary --
|
|
232
|
+
// callers must still send `Record<string, string | number | boolean>` for
|
|
233
|
+
// `connectionMetadata` and `CredentialRef` for `credentialRefs`; this catches
|
|
234
|
+
// obvious misuse only.
|
|
235
|
+
export const SECRET_KEY_PATTERN = /password|secret|token|private[_-]?key|dsn/i;
|
|
236
|
+
|
|
237
|
+
// Schemes whose URI form embeds credentials before `@`. Used as a value-side
|
|
238
|
+
// tripwire alongside `SECRET_KEY_PATTERN` -- callers should never send
|
|
239
|
+
// connection strings; the typed `connectionMetadata` shape is the contract.
|
|
240
|
+
export const CREDENTIAL_BEARING_DSN_SCHEMES = [
|
|
241
|
+
'postgres',
|
|
242
|
+
'postgresql',
|
|
243
|
+
'mysql',
|
|
244
|
+
'mariadb',
|
|
245
|
+
'mongodb',
|
|
246
|
+
'mongodb+srv',
|
|
247
|
+
'redis',
|
|
248
|
+
'rediss',
|
|
249
|
+
'amqp',
|
|
250
|
+
'amqps',
|
|
251
|
+
'kafka'
|
|
252
|
+
] as const;
|
|
253
|
+
|
|
254
|
+
export function isSecretKey(key: string): boolean {
|
|
255
|
+
return SECRET_KEY_PATTERN.test(key);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function containsCredentialMaterial(value: string): boolean {
|
|
259
|
+
if (DSN_WITH_INLINE_AUTH.test(value)) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
if (CREDENTIAL_KV_PATTERN.test(value)) {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
if (JSON_ENCODED_SECRET_PATTERN.test(value)) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const DSN_SCHEME_GROUP = CREDENTIAL_BEARING_DSN_SCHEMES.join('|').replace(/\+/g, '\\+');
|
|
272
|
+
const DSN_WITH_INLINE_AUTH = new RegExp(`(?:${DSN_SCHEME_GROUP}):\\/\\/[^/@\\s:]+:[^/@\\s]+@`, 'i');
|
|
273
|
+
const CREDENTIAL_KV_PATTERN = /(?:password|pwd|token|secret|private[_-]?key|api[_-]?key)\s*=\s*[^;&\s"]+/i;
|
|
274
|
+
const JSON_ENCODED_SECRET_PATTERN = /["']\s*(?:password|secret|token|private[_-]?key|dsn)\s*["']\s*:/i;
|
|
275
|
+
|
|
276
|
+
function isCredentialRefKey(key: string): boolean {
|
|
277
|
+
return /credential[_-]?ref/i.test(key);
|
|
278
|
+
}
|
package/src/index.ts
CHANGED
package/src/plugins/index.ts
CHANGED
|
@@ -117,6 +117,10 @@ export const CLOUD_PLUGIN_VERSIONS_LATEST: PluginExecutionVersions = {
|
|
|
117
117
|
[HubSpotPlugin.id]: MAX_VERSION,
|
|
118
118
|
[IntercomPlugin.id]: MAX_VERSION,
|
|
119
119
|
[JavascriptPlugin.id]: MAX_VERSION,
|
|
120
|
+
// The javascriptsdkapi plugin has no corresponding Plugin definition in
|
|
121
|
+
// packages/shared (it's only registered by the orchestrator). Keep the literal
|
|
122
|
+
// string in sync with SDK_API_PLUGIN_ID consumers.
|
|
123
|
+
javascriptsdkapi: MAX_VERSION,
|
|
120
124
|
[JiraPlugin.id]: MAX_VERSION,
|
|
121
125
|
[KafkaPlugin.id]: MAX_VERSION,
|
|
122
126
|
[KinesisPlugin.id]: MAX_VERSION,
|
|
@@ -11,9 +11,13 @@ export const DropboxPlugin: Plugin = getNativeOpenApiPlugin({
|
|
|
11
11
|
tokenUrl: 'https://www.dropbox.com/oauth2/token',
|
|
12
12
|
revokeTokenUrl: 'https://api.dropboxapi.com/2/auth/token/revoke',
|
|
13
13
|
iconUrl: `${PUBLIC_INTEGRATIONS_LOGO_URL}/dropbox.png`,
|
|
14
|
+
defaultScope: 'account_info.read files.metadata.read files.content.read files.content.write',
|
|
14
15
|
authorizationExtraParams: {
|
|
15
16
|
stateConfigExclude: ['datasource-auth-state'],
|
|
16
|
-
responseType: 'code'
|
|
17
|
+
responseType: 'code',
|
|
18
|
+
additionalUrlParams: {
|
|
19
|
+
token_access_type: 'offline'
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
}
|
|
19
23
|
},
|
|
@@ -68,6 +68,7 @@ export type AuthorizationExtraParams = {
|
|
|
68
68
|
accessType?: string;
|
|
69
69
|
stateConfigExclude?: AuthorizationStateConfig[];
|
|
70
70
|
owner?: string;
|
|
71
|
+
additionalUrlParams?: Record<string, string>;
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
type OAuth2Config = {
|
|
@@ -78,6 +79,7 @@ type OAuth2Config = {
|
|
|
78
79
|
userInfoUrl?: string;
|
|
79
80
|
authorizationExtraParams?: AuthorizationExtraParams;
|
|
80
81
|
iconUrl: string;
|
|
82
|
+
defaultScope?: string;
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
type OAuth2HostedClientConfig = OAuth2Config & { clientId: string };
|
|
@@ -1102,6 +1104,7 @@ export const authSections = ({
|
|
|
1102
1104
|
markdownText: `The scope of access request. It may have multiple space-separated values.`
|
|
1103
1105
|
},
|
|
1104
1106
|
singleLine: true,
|
|
1107
|
+
initialValue: enabledMethods.oauth2BringYourOwn?.defaultScope ?? enabledMethods.oauth2SuperblocksClient?.defaultScope,
|
|
1105
1108
|
display: {
|
|
1106
1109
|
show: {
|
|
1107
1110
|
authType: [IntegrationAuthType.OAUTH2_CODE]
|
package/src/types/ai/index.ts
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export enum AiProviderType {
|
|
2
|
+
SNOWFLAKE_CORTEX = 'snowflake_cortex',
|
|
3
|
+
DATABRICKS_AI_GATEWAY = 'databricks_ai_gateway'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const AI_PROVIDER_CREDENTIAL_STATUSES = ['untested', 'passing', 'failing'] as const;
|
|
7
|
+
export type AiProviderCredentialStatus = (typeof AI_PROVIDER_CREDENTIAL_STATUSES)[number];
|
|
8
|
+
|
|
9
|
+
export interface SnowflakeCortexBYOCredentials {
|
|
10
|
+
account: string;
|
|
11
|
+
username: string;
|
|
12
|
+
role?: string;
|
|
13
|
+
pat: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DatabricksAiGatewayBYOCredentials {
|
|
17
|
+
gatewayHost: string;
|
|
18
|
+
pat: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type BYOCredentials = SnowflakeCortexBYOCredentials | DatabricksAiGatewayBYOCredentials;
|
|
22
|
+
|
|
23
|
+
export interface AiProviderCredentialDto {
|
|
24
|
+
id: string;
|
|
25
|
+
provider: AiProviderType;
|
|
26
|
+
displayName: string;
|
|
27
|
+
providerMetadata?: Record<string, string>;
|
|
28
|
+
status: AiProviderCredentialStatus;
|
|
29
|
+
statusMessage?: string | null;
|
|
30
|
+
statusCheckedAt?: string | null;
|
|
31
|
+
isActive: boolean;
|
|
32
|
+
useManagedFallback: boolean;
|
|
33
|
+
fingerprint: string;
|
|
34
|
+
created: string;
|
|
35
|
+
updated: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CreateAiProviderCredentialRequest {
|
|
39
|
+
provider: AiProviderType;
|
|
40
|
+
displayName: string;
|
|
41
|
+
credentials: BYOCredentials;
|
|
42
|
+
useManagedFallback?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface UpdateAiProviderCredentialRequest {
|
|
46
|
+
displayName?: string;
|
|
47
|
+
credentials?: Partial<BYOCredentials>;
|
|
48
|
+
isActive?: boolean;
|
|
49
|
+
useManagedFallback?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface TestAiProviderCredentialConfigurationRequest {
|
|
53
|
+
provider: AiProviderType;
|
|
54
|
+
credentials: BYOCredentials;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AiProviderCredentialTestResult {
|
|
58
|
+
valid: boolean;
|
|
59
|
+
error?: string;
|
|
60
|
+
checkedAt: string;
|
|
61
|
+
}
|
package/src/types/audit/ocsf.ts
CHANGED
|
@@ -213,7 +213,13 @@ export const AUDIT_EVENT_TYPE_CATALOG: AuditEventTypeEntry[] = [
|
|
|
213
213
|
category_uid: OCSFCategoryUid.APPLICATION_ACTIVITY,
|
|
214
214
|
category_name: 'APPLICATION_ACTIVITY',
|
|
215
215
|
resource_type: 'Integration',
|
|
216
|
-
operations: [
|
|
216
|
+
operations: [
|
|
217
|
+
'integration.create',
|
|
218
|
+
'integration.delete',
|
|
219
|
+
'integration.ownership.update',
|
|
220
|
+
'integration.secret.view',
|
|
221
|
+
'integration.update'
|
|
222
|
+
]
|
|
217
223
|
},
|
|
218
224
|
{
|
|
219
225
|
class_uid: OCSFClassUid.ENTITY_MANAGEMENT,
|
|
@@ -259,7 +265,7 @@ export const AUDIT_EVENT_TYPE_CATALOG: AuditEventTypeEntry[] = [
|
|
|
259
265
|
category_uid: OCSFCategoryUid.IAM,
|
|
260
266
|
category_name: 'IAM',
|
|
261
267
|
resource_type: 'Token',
|
|
262
|
-
operations: ['token.create', 'token.delete', 'token.update']
|
|
268
|
+
operations: ['token.create', 'token.delete', 'token.regenerate', 'token.update']
|
|
263
269
|
},
|
|
264
270
|
{
|
|
265
271
|
class_uid: OCSFClassUid.USER_ACCESS_MANAGEMENT,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { DeploymentTypeEnum, isBrowserTelemetryEnabled } from './env.js';
|
|
4
|
+
|
|
5
|
+
describe('isBrowserTelemetryEnabled', () => {
|
|
6
|
+
test('returns true for CLOUD deployment', () => {
|
|
7
|
+
expect(isBrowserTelemetryEnabled(DeploymentTypeEnum.CLOUD)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('returns false for CLOUD_PREM deployment', () => {
|
|
11
|
+
expect(isBrowserTelemetryEnabled(DeploymentTypeEnum.CLOUD_PREM)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('returns false when deploymentType is undefined (fail-closed on ambiguity)', () => {
|
|
15
|
+
expect(isBrowserTelemetryEnabled(undefined)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("returns false for empty string (helm configmap renders '' when deploymentType override is missing)", () => {
|
|
19
|
+
expect(isBrowserTelemetryEnabled('' as DeploymentTypeEnum)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('returns false for common typos that would bypass a blocklist check', () => {
|
|
23
|
+
expect(isBrowserTelemetryEnabled('cloud_prem' as DeploymentTypeEnum)).toBe(false);
|
|
24
|
+
expect(isBrowserTelemetryEnabled('CLOUD-PREM' as DeploymentTypeEnum)).toBe(false);
|
|
25
|
+
expect(isBrowserTelemetryEnabled('cloud-prem ' as DeploymentTypeEnum)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
});
|
package/src/types/common/env.ts
CHANGED
|
@@ -7,7 +7,35 @@ export enum EnvEnum {
|
|
|
7
7
|
LOCAL = 'local'
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Deployment model for the running process.
|
|
12
|
+
*
|
|
13
|
+
* WARNING — opposing defaults by context: `parseDeploymentType` in
|
|
14
|
+
* `@superblocksteam/telemetry/common/deployment-type` (server-side) defaults a MISSING OR
|
|
15
|
+
* EMPTY value to `CLOUD` (fail-open — the server is trusted internal infrastructure) and
|
|
16
|
+
* THROWS on unrecognized values. `isBrowserTelemetryEnabled` in this module (browser-side)
|
|
17
|
+
* fails CLOSED — missing, empty, and unrecognized values all disable telemetry. Both are
|
|
18
|
+
* correct in their contexts (the browser reads values from an untrusted env-var pipeline),
|
|
19
|
+
* but the asymmetry is easy to miss. When adding new deployment-type-gated policies, pick
|
|
20
|
+
* the fail direction deliberately and document it.
|
|
21
|
+
*/
|
|
10
22
|
export enum DeploymentTypeEnum {
|
|
11
23
|
CLOUD = 'cloud',
|
|
12
24
|
CLOUD_PREM = 'cloud-prem'
|
|
13
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns whether browser telemetry (Datadog RUM, browser-logs) may be initialized.
|
|
29
|
+
* Allowlist-on-CLOUD: any non-CLOUD value — CLOUD_PREM, undefined, empty string,
|
|
30
|
+
* typos ('cloud_prem', 'CLOUD-PREM'), or future deployment types — disables telemetry.
|
|
31
|
+
* Cloud-prem deployments must not emit to Superblocks' public intake (customer network egress);
|
|
32
|
+
* fail-closed on ambiguity is the correct default for a privacy gate.
|
|
33
|
+
*
|
|
34
|
+
* Parameter widened to `string | undefined` because call sites pull from env.get() with an
|
|
35
|
+
* `as DeploymentTypeEnum` cast — any string can actually arrive (empty, typo, whitespace,
|
|
36
|
+
* unexpected value). The strict equality still narrows correctly; the wider type makes the
|
|
37
|
+
* runtime truth explicit at the boundary.
|
|
38
|
+
*/
|
|
39
|
+
export const isBrowserTelemetryEnabled = (deploymentType: string | undefined): boolean => {
|
|
40
|
+
return deploymentType === DeploymentTypeEnum.CLOUD;
|
|
41
|
+
};
|
|
@@ -51,11 +51,11 @@ export class ResponseMeta {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export class ErrorDto {
|
|
54
|
-
code: number;
|
|
54
|
+
code: number | string;
|
|
55
55
|
message: string;
|
|
56
56
|
superblocksError?: SuperblocksError;
|
|
57
57
|
|
|
58
|
-
constructor({ code, message, type }: { code: number; message: string; type?: SuperblocksError }) {
|
|
58
|
+
constructor({ code, message, type }: { code: number | string; message: string; type?: SuperblocksError }) {
|
|
59
59
|
this.code = code;
|
|
60
60
|
this.message = message;
|
|
61
61
|
this.superblocksError = type;
|
|
@@ -100,20 +100,20 @@ export type InferenceSnowflakeCortexPayload = {
|
|
|
100
100
|
/**
|
|
101
101
|
* Databricks AI Gateway credential. AI Gateway only accepts Programmatic
|
|
102
102
|
* Access Tokens (PATs) as bearer credentials — there is no keypair or
|
|
103
|
-
* OAuth path comparable to Snowflake Cortex. `
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* OAuth path comparable to Snowflake Cortex. `gatewayHost` is either the
|
|
104
|
+
* Anthropic-compatible AI Gateway origin or a workspace AI Gateway
|
|
105
|
+
* Anthropic messages URL, both validated at the registry.
|
|
106
106
|
*/
|
|
107
107
|
export type InferenceDatabricksAiGatewayPayload = {
|
|
108
108
|
type: 'inference.databricks-ai-gateway.pat';
|
|
109
109
|
secrets: { pat: string };
|
|
110
110
|
metadata: {
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
113
|
-
* or
|
|
114
|
-
*
|
|
112
|
+
* AI Gateway origin, e.g. `https://<gateway-name>.ai-gateway.cloud.databricks.com`,
|
|
113
|
+
* or workspace Anthropic messages URL, e.g.
|
|
114
|
+
* `https://<your-databricks-workspace-url>/ai-gateway/anthropic/v1/messages`.
|
|
115
115
|
*/
|
|
116
|
-
|
|
116
|
+
gatewayHost: string;
|
|
117
117
|
};
|
|
118
118
|
};
|
|
119
119
|
|
package/src/types/index.ts
CHANGED
|
@@ -22,10 +22,12 @@ export * from './lock/index.js';
|
|
|
22
22
|
export * from './observability/index.js';
|
|
23
23
|
export * from './organization/index.js';
|
|
24
24
|
export * from './page/index.js';
|
|
25
|
+
export * from './policyGate/index.js';
|
|
25
26
|
export * from './plugin/index.js';
|
|
26
27
|
export * from './profile/index.js';
|
|
27
28
|
export * from './rbac/index.js';
|
|
28
29
|
export * from './response/index.js';
|
|
30
|
+
export * from './reviewPolicy/index.js';
|
|
29
31
|
export * from './remoteCommit/index.js';
|
|
30
32
|
export * from './scim/index.js';
|
|
31
33
|
export * from './signingKeyRotationJob/index.js';
|
|
@@ -89,6 +89,7 @@ class IntegrationBaseDto {
|
|
|
89
89
|
name: string;
|
|
90
90
|
pluginId: string;
|
|
91
91
|
organizationId: string;
|
|
92
|
+
applicationId?: string | null;
|
|
92
93
|
isUserConfigured: boolean;
|
|
93
94
|
demoIntegrationId?: string;
|
|
94
95
|
}
|
|
@@ -125,7 +126,8 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
125
126
|
editable,
|
|
126
127
|
permissions,
|
|
127
128
|
creator,
|
|
128
|
-
enabledForV2
|
|
129
|
+
enabledForV2,
|
|
130
|
+
applicationId
|
|
129
131
|
}: {
|
|
130
132
|
id: string;
|
|
131
133
|
created: Date;
|
|
@@ -145,6 +147,7 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
145
147
|
permissions?: ActionTypeEnum[];
|
|
146
148
|
creator?: CreatorDto;
|
|
147
149
|
enabledForV2?: boolean;
|
|
150
|
+
applicationId?: string | null;
|
|
148
151
|
}) {
|
|
149
152
|
super();
|
|
150
153
|
this.id = id;
|
|
@@ -154,6 +157,7 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
154
157
|
this.pluginId = pluginId;
|
|
155
158
|
this.kind = kind;
|
|
156
159
|
this.organizationId = organizationId;
|
|
160
|
+
this.applicationId = applicationId;
|
|
157
161
|
this.configurations = configurations;
|
|
158
162
|
this.demoIntegrationId = demoIntegrationId;
|
|
159
163
|
this.isUserConfigured = isUserConfigured;
|
|
@@ -202,7 +206,8 @@ export class IntegrationDto extends IntegrationBaseDto {
|
|
|
202
206
|
creator,
|
|
203
207
|
kind,
|
|
204
208
|
slug,
|
|
205
|
-
enabledForV2
|
|
209
|
+
enabledForV2,
|
|
210
|
+
applicationId
|
|
206
211
|
}: {
|
|
207
212
|
id: string;
|
|
208
213
|
created: Date;
|
|
@@ -218,6 +223,7 @@ export class IntegrationDto extends IntegrationBaseDto {
|
|
|
218
223
|
kind: IntegrationKind;
|
|
219
224
|
slug?: string;
|
|
220
225
|
enabledForV2?: boolean;
|
|
226
|
+
applicationId?: string | null;
|
|
221
227
|
}) {
|
|
222
228
|
super();
|
|
223
229
|
this.id = id;
|
|
@@ -226,6 +232,7 @@ export class IntegrationDto extends IntegrationBaseDto {
|
|
|
226
232
|
this.name = name;
|
|
227
233
|
this.pluginId = pluginId;
|
|
228
234
|
this.organizationId = organizationId;
|
|
235
|
+
this.applicationId = applicationId;
|
|
229
236
|
this.demoIntegrationId = demoIntegrationId;
|
|
230
237
|
this.configurations = configurations;
|
|
231
238
|
this.isUserConfigured = isUserConfigured;
|
package/src/types/plugin/form.ts
CHANGED