Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/publish-sim-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ on:
branches: [main, staging, dev]
paths:
- 'packages/sim-cli/**'
# The build bundles its workspace dependencies, so a change there
# changes what ships.
- 'packages/utils/**'
# A change to how the package is built or published has to republish it.
- '.github/workflows/publish-sim-cli.yml'

permissions:
contents: read
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ jobs:
lib/auth/oauth-provider-lifecycle.postgres.test.ts
app/api/auth/oauth2/token/route.postgres.test.ts
lib/auth/sim-auth-adapter.test.ts
lib/auth/sim-auth-adapter.postgres.test.ts
ee/scim/lib/managed-membership.postgres.test.ts
lib/auth/sso/application/admit-sso-user.postgres.test.ts

Expand Down
1 change: 0 additions & 1 deletion apps/sim/app/(landing)/components/shared/zip-icon/index.ts

This file was deleted.

28 changes: 0 additions & 28 deletions apps/sim/app/(landing)/components/shared/zip-icon/zip-icon.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions apps/sim/app/(landing)/files/components/files-hero-loop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { useState } from 'react'
import { cn } from '@sim/emcn'
import { ArrowUpDown, File, ListFilter, Plus, Search } from '@sim/emcn/icons'
import { AgentIcon } from '@/components/icons'
import { CsvIcon, DocxIcon, PdfIcon } from '@/components/icons/document-icons'
import { CsvIcon, DocxIcon, PdfIcon, ZipIcon } from '@/components/icons/document-icons'
import { HeroLoopShell } from '@/app/(landing)/components/shared/hero-loop-shell'
import { PLATFORM_LOOP_RESET_FADE_MS } from '@/app/(landing)/components/shared/platform-loop-constants'
import { ZipIcon } from '@/app/(landing)/components/shared/zip-icon'
import { useMotionSafeCycle } from '@/app/(landing)/hooks/use-motion-safe-cycle'

/** Sidebar content for the files hero - a file-heavy team's workspace. */
Expand Down
229 changes: 0 additions & 229 deletions apps/sim/app/api/table/row-secret-provenance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
import { createMockRequest } from '@sim/testing'
import { describe, expect, it } from 'vitest'
import { AuthType } from '@/lib/auth/hybrid'
import {
PRIVATE_SECRET_PROVENANCE_BUNDLE_V1,
PRIVATE_SECRET_PROVENANCE_FIELD,
Expand All @@ -13,29 +12,16 @@ import {
RESOLVED_SECRET_PROVENANCE_METADATA_V1,
} from '@/lib/execution/private-tool-metadata'
import { TableRowProvenanceError } from '@/lib/table/application/row-secret-provenance'
import { rowDataNameToId } from '@/lib/table/column-keys'
import { tableRowSecretProvenanceSelectionKey } from '@/lib/table/secret-provenance-selection'
import type { RowData } from '@/lib/table/types'
import {
createTableWriteProvenanceTargets,
finalizeTableRowsProvenance,
negotiateTableRowsProvenance,
readTableRowProvenanceEnvelope,
resolveTableWriteSecretProvenance,
} from '@/app/api/table/row-secret-provenance'

const USER_ID = 'user-1'
const WORKSPACE_ID = 'ws-1'

/** Mirrors the internal-JWT wire translator: names → ids, unknown names dropped. */
const ID_BY_NAME = new Map([
['email', 'col_email'],
['company', 'col_company'],
])

const translateNames = (data: RowData): RowData => rowDataNameToId(data, ID_BY_NAME)
const translateIdentity = (data: RowData): RowData => data

function traceProvenance() {
return {
version: 1,
Expand All @@ -59,221 +45,6 @@ function bundleRequest(selectionKeys: string[]) {
return { request, payload }
}

describe('createTableWriteProvenanceTargets', () => {
it('maps column names to their storage ids', () => {
const targets = createTableWriteProvenanceTargets([{ email: 'a@b.c' }], translateNames)

expect(targets).toEqual([
{
selectionKey: tableRowSecretProvenanceSelectionKey(0, 'email'),
rowKey: '0',
columnId: 'col_email',
},
])
})

it('returns a null column id for a column the wire translator drops', () => {
const targets = createTableWriteProvenanceTargets(
[{ email: 'a@b.c', notAColumn: 'x' }],
translateNames
)

expect(targets).toHaveLength(2)
expect(targets[0].columnId).toBe('col_email')
expect(targets[1]).toEqual({
selectionKey: tableRowSecretProvenanceSelectionKey(0, 'notAColumn'),
rowKey: '0',
columnId: null,
})
})

it('keeps one target per submitted column so bundle selections stay paired', () => {
const targets = createTableWriteProvenanceTargets(
[{ notAColumn: 'x', alsoNotAColumn: 'y' }],
translateNames
)

expect(targets.map((target) => target.columnId)).toEqual([null, null])
})

it('passes column ids through for identity (session) translation', () => {
const targets = createTableWriteProvenanceTargets([{ col_email: 'a@b.c' }], translateIdentity)

expect(targets[0].columnId).toBe('col_email')
})

it('keys targets by row index across multiple rows', () => {
const targets = createTableWriteProvenanceTargets(
[{ email: 'a@b.c' }, { company: 'Acme' }],
translateNames
)

expect(targets.map((target) => target.rowKey)).toEqual(['0', '1'])
expect(targets[1].selectionKey).toBe(tableRowSecretProvenanceSelectionKey(1, 'company'))
})
})

describe('resolveTableWriteSecretProvenance', () => {
it('records no provenance for a dropped column on an unsupported session write', () => {
const rows = [{ email: 'a@b.c', notAColumn: 'x' }]
const result = resolveTableWriteSecretProvenance({
request: createMockRequest('POST', { rows }),
payload: { rows },
authType: AuthType.SESSION,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(true)
if (!result.success) return
expect(Object.keys(result.provenanceByRowKey?.['0'].columns ?? {})).toEqual(['col_email'])
})

it('accepts a complete bundle that covers a dropped column', () => {
const rows = [{ email: 'a@b.c', notAColumn: 'x' }]
const { request, payload } = bundleRequest([
tableRowSecretProvenanceSelectionKey(0, 'email'),
tableRowSecretProvenanceSelectionKey(0, 'notAColumn'),
])

const result = resolveTableWriteSecretProvenance({
request,
payload,
authType: AuthType.INTERNAL_JWT,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(true)
if (!result.success) return
expect(Object.keys(result.provenanceByRowKey?.['0'].columns ?? {})).toEqual(['col_email'])
})

it('stores provenance for a fully translatable bundle', () => {
const rows = [{ email: 'a@b.c', company: 'Acme' }]
const { request, payload } = bundleRequest([
tableRowSecretProvenanceSelectionKey(0, 'email'),
tableRowSecretProvenanceSelectionKey(0, 'company'),
])

const result = resolveTableWriteSecretProvenance({
request,
payload,
authType: AuthType.INTERNAL_JWT,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(true)
if (!result.success) return
expect(Object.keys(result.provenanceByRowKey?.['0'].columns ?? {}).sort()).toEqual([
'col_company',
'col_email',
])
})

it('rejects a bundle whose selection matches no submitted column', () => {
const rows = [{ email: 'a@b.c' }]
const { request, payload } = bundleRequest([tableRowSecretProvenanceSelectionKey(0, 'company')])

const result = resolveTableWriteSecretProvenance({
request,
payload,
authType: AuthType.INTERNAL_JWT,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(false)
})

it('accepts a different source user in the authorized destination workspace', () => {
const rows = [{ email: 'a@b.c' }]
const payload = {
[PRIVATE_SECRET_PROVENANCE_FIELD]: {
version: 1,
complete: true,
selections: [
{
key: tableRowSecretProvenanceSelectionKey(0, 'email'),
provenance: {
...traceProvenance(),
scope: { userId: 'someone-else', workspaceId: WORKSPACE_ID },
},
},
],
},
}

const result = resolveTableWriteSecretProvenance({
request: createMockRequest('POST', payload, {
[PRIVATE_SECRET_PROVENANCE_HEADER]: PRIVATE_SECRET_PROVENANCE_BUNDLE_V1,
}),
payload,
authType: AuthType.INTERNAL_JWT,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(true)
if (!result.success) return
expect(result.provenanceByRowKey?.['0'].columns.col_email).toMatchObject({
scope: { userId: 'someone-else', workspaceId: WORKSPACE_ID },
})
})

it('rejects a bundle whose selection comes from another workspace', () => {
const rows = [{ email: 'a@b.c' }]
const payload = {
[PRIVATE_SECRET_PROVENANCE_FIELD]: {
version: 1,
complete: true,
selections: [
{
key: tableRowSecretProvenanceSelectionKey(0, 'email'),
provenance: {
...traceProvenance(),
scope: { userId: USER_ID, workspaceId: 'another-workspace' },
},
},
],
},
}

const result = resolveTableWriteSecretProvenance({
request: createMockRequest('POST', payload, {
[PRIVATE_SECRET_PROVENANCE_HEADER]: PRIVATE_SECRET_PROVENANCE_BUNDLE_V1,
}),
payload,
authType: AuthType.INTERNAL_JWT,
userId: USER_ID,
workspaceId: WORKSPACE_ID,
targets: createTableWriteProvenanceTargets(rows, translateNames),
rowKeys: ['0'],
})

expect(result.success).toBe(false)
})
})

/**
* The transport half of the envelope, used by the migrated single-row routes.
*
* These are the only cover these helpers have: the route tests assert `mapInput`
* and `present`, so each helper could be replaced by a constant without a route
* test noticing — and a constant `readTableRowProvenanceEnvelope` would silently
* downgrade every executor write from a stamped bundle to untracked.
*/
describe('readTableRowProvenanceEnvelope', () => {
it('reports no envelope when the caller sent none', () => {
const request = createMockRequest('PATCH', { data: {} })
Expand Down
Loading
Loading