Skip to content
Draft
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
99 changes: 99 additions & 0 deletions e2e/playwright/desktop-export.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,102 @@
})
}
)

test(
'DXF export works from feature tree sketch context menu',
{ tag: ['@desktop', '@macos', '@windows', '@skipLocalEngine'] },
async ({ page, context, scene, tronApp, cmdBar, toolbar }, testInfo) => {
if (!tronApp) {
fail()
}

await context.folderSetupFn(async (dir) => {
const sketchDir = path.join(dir, 'sketch-project')

Check warning on line 173 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

path-join-resolve-traversal

Detected possible user input going into a path.join or path.resolve function. This could possibly lead to a path traversal vulnerability where the attacker can access arbitrary files stored in the file system. Instead be sure to sanitize or validate user input first.
await fsp.mkdir(sketchDir, { recursive: true })

Check warning on line 174 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

detect-non-literal-fs-filename

Detected that function argument dir has entered the fs module. An attacker could potentially control the location of this file to include going backwards in the directory with ... To address this ensure that usercontrolled variables in file paths are validated.
await fsp.writeFile(
path.join(sketchDir, 'main.kcl'),

Check warning on line 176 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

path-join-resolve-traversal

Detected possible user input going into a path.join or path.resolve function. This could possibly lead to a path traversal vulnerability where the attacker can access arbitrary files stored in the file system. Instead be sure to sanitize or validate user input first.

Check warning on line 176 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

detect-non-literal-fs-filename

Detected that function argument dir has entered the fs module. An attacker could potentially control the location of this file to include going backwards in the directory with ... To address this ensure that usercontrolled variables in file paths are validated.
`sketch001 = startSketchOn(XY)
profile001 = startProfile(sketch001, at = [-18.26, 13.11])
|> line(end = [9.86, -32.02])
|> xLine(length = 18.11)
|> line(end = [11.44, 33.04])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(profile001, length = 5)`
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })

// Open the project
const projectName = page.getByText(`sketch-project`)
await expect(projectName).toBeVisible()
await projectName.click()
await scene.settled(cmdBar)

// Expect zero errors in gutter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()

// Close other panes and ensure only feature tree is open
const u = await getUtils(page)
await u.closeFilePanel()
await u.closeDebugPanel()
await u.closeKclCodePanel()

// Open the feature tree pane
await toolbar.openFeatureTreePane()

// Find the sketch operation in the feature tree
const sketchNode = page.getByText('sketch001').first()
await expect(sketchNode).toBeVisible()

// Right-click to open context menu
await sketchNode.click({ button: 'right' })

// Verify that "Export to DXF" option is present and click it
const dxfExportOption = page.getByTestId('context-menu-export-dxf')
await expect(dxfExportOption).toBeVisible()
await dxfExportOption.click()

// Look out for the loading toast message
const exportingToastMessage = page.getByText('Exporting sketch to DXF...')
await expect(exportingToastMessage).toBeVisible()

// Expect it to succeed - check for various error types
const errorToastMessage = page.getByText('Failed to export sketch to DXF')
const generalErrorToastMessage = page.getByText('Error while exporting')
const engineErrorToastMessage = page.getByText('Nothing to export')
await expect(errorToastMessage).not.toBeVisible()
await expect(generalErrorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()

const successToastMessage = page.getByText('DXF export completed [TEST]')
await page.waitForTimeout(1_000)
const count = await successToastMessage.count()
await expect(count).toBeGreaterThanOrEqual(1)
await expect(exportingToastMessage).not.toBeVisible()

// Check for the exported DXF file
const exportFileName = 'sketch001.dxf'
const dxfFileFullPath = path.resolve(
getPlaywrightDownloadDir(tronApp.projectDirName),

Check warning on line 240 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

path-join-resolve-traversal

Detected possible user input going into a path.join or path.resolve function. This could possibly lead to a path traversal vulnerability where the attacker can access arbitrary files stored in the file system. Instead be sure to sanitize or validate user input first.
exportFileName
)

await test.step('Check the DXF export size', async () => {
await expect
.poll(
async () => {
try {
const outputDxf = await fsp.readFile(dxfFileFullPath)

Check warning on line 249 in e2e/playwright/desktop-export.spec.ts

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

detect-non-literal-fs-filename

Detected that function argument page context scene tronApp cmdBar toolbar has entered the fs module. An attacker could potentially control the location of this file to include going backwards in the directory with ... To address this ensure that usercontrolled variables in file paths are validated.
return outputDxf.byteLength
} catch (error: unknown) {
void error
return 0
}
},
{ timeout: 15_000 }
)
.toBeGreaterThan(500) // DXF files should have meaningful content
})
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
getOperationVariableName,
stdLibMap,
} from '@src/lib/operations'
import { uuidv4 } from '@src/lib/utils'
import {
codeManager,
commandBarActor,
editorManager,
engineCommandManager,
kclManager,
rustContext,
sceneInfra,
Expand All @@ -48,6 +50,11 @@
import type { DefaultPlaneStr } from '@src/lib/planes'
import { findOperationPlaneArtifact, isOffsetPlane } from '@src/lang/queryAst'
import { err } from '@src/lib/trap'
import toast from 'react-hot-toast'
import { base64Decode } from '@src/lang/wasm'
import { browserSaveFile } from '@src/lib/browserSaveFile'
import { isDesktop } from '@src/lib/isDesktop'
import { exportSketchToDxf } from '@src/lib/exportDxf'

export const FeatureTreePane = () => {
const isEditorMounted = useSelector(kclEditorActor, editorIsMountedSelector)
Expand Down Expand Up @@ -590,6 +597,33 @@
</ContextMenuItem>,
]
: []),
...(props.item.type === 'StdLibCall' &&
props.item.name === 'startSketchOn'
? [
<ContextMenuItem
onClick={() => {
const exportDxf = async () => {
if (props.item.type !== 'StdLibCall') return
await exportSketchToDxf(props.item, {
engineCommandManager,
kclManager,
toast,
uuidv4,
base64Decode,
isDesktop,
browserSaveFile,
writeFile: window.electron?.writeFile,
showSaveDialog: window.electron?.save,
})
}
void exportDxf()
}}
data-testid="context-menu-export-dxf"
>
Export to DXF
</ContextMenuItem>,

Check warning on line 624 in src/components/ModelingSidebar/ModelingPanes/FeatureTreePane.tsx

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

jsx-not-internationalized

JSX element not internationalized Export to DXF . You should support different languages in your website or app with internationalization. Instead use packages such as i18next in order to internationalize your elements.
]
: []),
...(props.item.type === 'StdLibCall' ||
props.item.type === 'VariableDeclaration'
? [
Expand Down
Loading
Loading