Skip to content

Commit 3d12a6d

Browse files
authored
v0.7.40: library, mcp hardening, pi code reviewer, gmail fix
2 parents 4ddad74 + 48103e9 commit 3d12a6d

287 files changed

Lines changed: 14292 additions & 2493 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/content/docs/en/workflows/blocks/pi.mdx

Lines changed: 70 additions & 40 deletions
Large diffs are not rendered by default.

apps/sim/app/(landing)/components/content-author-page/content-author-page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export function ContentAuthorPage({
7373
month: 'short',
7474
day: 'numeric',
7575
year: 'numeric',
76+
timeZone: 'UTC',
7677
})}
7778
</span>
7879

@@ -82,6 +83,7 @@ export function ContentAuthorPage({
8283
month: 'short',
8384
day: 'numeric',
8485
year: 'numeric',
86+
timeZone: 'UTC',
8587
})}
8688
</span>
8789
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg'>

apps/sim/app/(landing)/components/content-index-page/content-index-page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ export function ContentIndexPage({
8484
<span className='text-[var(--text-muted)] text-xs uppercase tracking-[0.1em]'>
8585
{new Date(p.date).toLocaleDateString('en-US', {
8686
month: 'short',
87-
year: '2-digit',
87+
day: 'numeric',
88+
year: 'numeric',
89+
timeZone: 'UTC',
8890
})}
8991
</span>
9092
<h3 className='text-[var(--text-primary)] text-lg leading-tight tracking-[-0.01em]'>
@@ -113,6 +115,7 @@ export function ContentIndexPage({
113115
month: 'short',
114116
day: 'numeric',
115117
year: 'numeric',
118+
timeZone: 'UTC',
116119
})}
117120
</span>
118121

@@ -122,6 +125,7 @@ export function ContentIndexPage({
122125
month: 'short',
123126
day: 'numeric',
124127
year: 'numeric',
128+
timeZone: 'UTC',
125129
})}
126130
</span>
127131
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg'>

apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function ContentPostPage({
8383
month: 'short',
8484
day: 'numeric',
8585
year: 'numeric',
86+
timeZone: 'UTC',
8687
})}
8788
</time>
8889
<meta itemProp='dateModified' content={post.updated ?? post.date} />
@@ -152,7 +153,9 @@ export function ContentPostPage({
152153
<span className='text-[var(--text-muted)] text-xs uppercase tracking-[0.1em]'>
153154
{new Date(p.date).toLocaleDateString('en-US', {
154155
month: 'short',
155-
year: '2-digit',
156+
day: 'numeric',
157+
year: 'numeric',
158+
timeZone: 'UTC',
156159
})}
157160
</span>
158161
<h3 className='text-[var(--text-primary)] text-lg leading-tight tracking-[-0.01em]'>

apps/sim/app/_styles/globals.css

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@
2222
--auth-primary-btn-hover-border: #e0e0e0;
2323
--auth-primary-btn-hover-text: #000000;
2424

25-
/* z-index scale. Transient poppers (menus, selects, popovers, tooltips, toasts)
26-
sit above --z-modal so they stay clickable over the semi-transparent overlay. */
25+
/* z-index scale. The toast/notification stack is ambient: it sits above page
26+
content but BELOW the modal and the transient poppers (menus, selects,
27+
popovers, tooltips), so an open modal or dropdown menu is never occluded by
28+
a background notification. Poppers sit above --z-modal so they stay
29+
clickable over the modal's semi-transparent overlay. */
2730
--z-dropdown: 100;
31+
--z-toast: 150;
2832
--z-modal: 200;
2933
--z-popover: 300;
3034
--z-tooltip: 400;
31-
--z-toast: 500;
3235

3336
/* Shadow scale */
3437
--shadow-subtle: 0 2px 4px 0 rgba(0, 0, 0, 0.08);

apps/sim/app/api/files/parse/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { checkInternalAuth } from '@/lib/auth/hybrid'
1313
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
1414
import { assertKnownSizeWithinLimit, isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits'
1515
import { isSupportedFileType, parseFile } from '@/lib/file-parsers'
16+
import { isYamlComplexityError } from '@/lib/file-parsers/yaml-parser'
1617
import { isUsingCloudStorage, StorageService } from '@/lib/uploads'
1718
import { uploadExecutionFile } from '@/lib/uploads/contexts/execution'
1819
import {
@@ -1043,6 +1044,9 @@ async function handleGenericTextBuffer(
10431044
}
10441045
} catch (parserError) {
10451046
if (isPayloadSizeLimitError(parserError)) throw parserError
1047+
// Fail closed on a resource-exhaustion rejection instead of silently
1048+
// storing the crafted document as raw text.
1049+
if (isYamlComplexityError(parserError)) throw parserError
10461050

10471051
logger.warn('Specialized parser failed, falling back to generic parsing:', parserError)
10481052
}

apps/sim/app/api/files/presigned/route.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,9 @@ describe('/api/files/presigned', () => {
588588

589589
const response = await POST(request)
590590
expect(response.status).toBe(200)
591-
expect(mockValidateAttachmentFileType).toHaveBeenCalledWith('screenshot.png')
591+
expect(mockValidateAttachmentFileType).toHaveBeenCalledWith('screenshot.png', {
592+
allowArchives: true,
593+
})
592594
expect(mockValidateFileType).not.toHaveBeenCalled()
593595
})
594596

apps/sim/app/api/files/presigned/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
142142
)
143143
}
144144

145-
const fileValidationError = validateAttachmentFileType(fileName)
145+
const fileValidationError = validateAttachmentFileType(fileName, { allowArchives: true })
146146
if (fileValidationError) {
147147
throw new ValidationError(fileValidationError.message)
148148
}

apps/sim/app/api/files/upload/route.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type { StorageContext } from '@/lib/uploads/config'
2323
import { generateKnowledgeBaseFileKey } from '@/lib/uploads/contexts/knowledge-base/knowledge-base-file-manager'
2424
import { generateWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
2525
import { MAX_WORKSPACE_FORMDATA_FILE_SIZE } from '@/lib/uploads/shared/types'
26-
import { isImageFileType, resolveFileType } from '@/lib/uploads/utils/file-utils'
26+
import { isArchiveFileName, isImageFileType, resolveFileType } from '@/lib/uploads/utils/file-utils'
2727
import {
2828
SUPPORTED_ATTACHMENT_EXTENSIONS,
2929
SUPPORTED_IMAGE_EXTENSIONS,
@@ -34,9 +34,12 @@ import { createErrorResponse, InvalidRequestError } from '@/app/api/files/utils'
3434

3535
const ALLOWED_EXTENSIONS = new Set<string>(SUPPORTED_ATTACHMENT_EXTENSIONS)
3636

37-
function validateFileExtension(filename: string): boolean {
37+
function validateFileExtension(filename: string, context: StorageContext): boolean {
3838
const extension = filename.split('.').pop()?.toLowerCase()
3939
if (!extension) return false
40+
// Archives are only extractable in the mothership copilot flow; every other
41+
// context keeps rejecting them up front instead of failing downstream.
42+
if (context === 'mothership' && isArchiveFileName(filename)) return true
4043
return ALLOWED_EXTENSIONS.has(extension)
4144
}
4245

@@ -150,7 +153,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
150153
for (const file of files) {
151154
const originalName = file.name || 'untitled.md'
152155

153-
if (!validateFileExtension(originalName)) {
156+
if (!validateFileExtension(originalName, context)) {
154157
const extension = originalName.split('.').pop()?.toLowerCase() || 'unknown'
155158
throw new InvalidRequestError(
156159
`File type '${extension}' is not allowed. Allowed types: ${Array.from(ALLOWED_EXTENSIONS).join(', ')}`

apps/sim/app/api/mcp/oauth/callback/route.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,45 @@ describe('MCP OAuth callback route', () => {
7272
})
7373
)
7474
})
75+
76+
it('signals success over a same-origin BroadcastChannel carrying the state nonce', async () => {
77+
const request = new NextRequest(
78+
'http://localhost:3000/api/mcp/oauth/callback?state=state-1&code=auth-code-1'
79+
)
80+
81+
const body = await (await GET(request)).text()
82+
83+
// The completion is delivered over a BroadcastChannel (not window.opener.postMessage)
84+
// so a COOP `same-origin` provider that severs the opener can't strand the parent. The
85+
// `state` nonce lets the hook react only in the tab that started this exact flow.
86+
expect(body).toContain("new BroadcastChannel('mcp-oauth')")
87+
expect(body).toContain('ok: true')
88+
expect(body).toContain('"server-1"')
89+
expect(body).toContain('"state-1"')
90+
})
91+
92+
it('reports an early failure over the channel without attempting token exchange', async () => {
93+
// Missing `code` fails at the param gate, before any network work.
94+
const request = new NextRequest('http://localhost:3000/api/mcp/oauth/callback?state=state-1')
95+
96+
const body = await (await GET(request)).text()
97+
98+
expect(body).toContain('ok: false')
99+
expect(mcpOauthMockFns.mockMcpAuthGuarded).not.toHaveBeenCalled()
100+
})
101+
102+
it('echoes the state on a serverless invalid_state failure so the initiating tab can react', async () => {
103+
// No row loads for the state -> failure with no serverId. The state must still be echoed,
104+
// or the initiating tab would sit on "Connecting…" until its safety timeout.
105+
mcpOauthMockFns.mockLoadOauthRowByState.mockResolvedValueOnce(null)
106+
const request = new NextRequest(
107+
'http://localhost:3000/api/mcp/oauth/callback?state=state-1&code=auth-code-1'
108+
)
109+
110+
const body = await (await GET(request)).text()
111+
112+
expect(body).toContain('ok: false')
113+
expect(body).toContain('"state-1"')
114+
expect(body).toContain('serverId: undefined')
115+
})
75116
})

0 commit comments

Comments
 (0)