diff --git a/apps/docs/docs/intro.md b/apps/docs/docs/intro.md index b03778f25..7e63a10f4 100644 --- a/apps/docs/docs/intro.md +++ b/apps/docs/docs/intro.md @@ -104,6 +104,49 @@ pnpm run snyk:iac # IaC - scan Bicep templates for misconfigurations > **Note**: Only use the npm scripts listed above. Other Snyk scripts (`snyk:monitor`, `snyk:code:report`) are reserved for CI/CD pipeline use only. +## Local EdgeScan Setup + +EdgeScan is a **Dynamic Application Security Testing (DAST)** platform. Unlike Snyk or SonarCloud, which analyze source code, EdgeScan scans **live, running applications** to provide deep security intelligence and continuous vulnerability profiling for our serverless architecture. + +**How to use:** +- Use `pnpm run edgescan:dev` to run local security validation scans. +- **DO NOT use** `edgescan:agent` - this script is strictly reserved for the **GitHub Copilot AI Coding Agent** and CI/CD automation. + +### Prerequisites + +This setup is required for the `edgescan:dev` script. + +#### 1. Apple Native Containers + +This is a one-time setup for macOS developers. + +1. Download the `container-installer-signed.pkg` from the [Apple Native Containers releases](https://github.com/apple/container/releases). +2. Run the installer. +3. Once finished, start the container system: + ```bash + container system start + ``` + Input `Y` when prompted. +4. Confirm it is working as expected: + ```bash + container system status + ``` + Expected output: + ```text + ❯ container system status + apiserver is running + ... + ``` + +#### 2. EdgeScan API Token + +1. Log in to [intealth.edgescan.com](https://intealth.edgescan.com). +2. Go to your **Profile Settings** and generate an API token for your account. +3. Export the token in your terminal (consider adding this to your `~/.zshrc` or `~/.bashrc`): + ```bash + export ES_API_TOKEN="" + ``` + ## Start Development Run the development environment: diff --git a/apps/ui-community/package.json b/apps/ui-community/package.json index 2881915f5..f5a6bf716 100644 --- a/apps/ui-community/package.json +++ b/apps/ui-community/package.json @@ -7,13 +7,13 @@ "prebuild": "biome lint", "build": "tsc --build && vite build", "start": "vite", - "lint": "biome lint", - "preview": "vite preview", - "test": "vitest run --silent --reporter=dot", - "test:coverage": "vitest run --coverage --silent --reporter=dot", - "test:watch": "vitest", - "storybook": "storybook dev -p 6008", - "build-storybook": "storybook build" + "lint": "biome lint", + "preview": "vite preview", + "test": "vitest run --silent --reporter=dot", + "test:coverage": "vitest run --coverage --silent --reporter=dot", + "test:watch": "vitest", + "storybook": "storybook dev -p 6008", + "build-storybook": "storybook build" }, "dependencies": { "@ant-design/icons": "^6.0.2", diff --git a/build-pipeline/core/monorepo-build-stage.yml b/build-pipeline/core/monorepo-build-stage.yml index c972c790a..9e9e5fe72 100644 --- a/build-pipeline/core/monorepo-build-stage.yml +++ b/build-pipeline/core/monorepo-build-stage.yml @@ -434,7 +434,7 @@ stages: # Deploy API package with production dependencies - task: Bash@3 displayName: 'Artifact: Prepare API' - condition: and(succeeded(), eq(variables['BuildJob.HAS_BACKEND_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_BACKEND_CHANGES'], 'true')) inputs: targetType: 'inline' script: | @@ -495,7 +495,7 @@ stages: # Package UI Community compiled assets into artifact - task: ArchiveFiles@2 displayName: 'Artifact: Prepare UI Community' - condition: and(succeeded(), eq(variables['BuildJob.HAS_FRONTEND_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_FRONTEND_CHANGES'], 'true')) inputs: rootFolderOrFile: 'apps/ui-community/dist' includeRootFolder: false @@ -506,7 +506,7 @@ stages: # Package Docs compiled assets into artifact - task: ArchiveFiles@2 displayName: 'Artifact: Prepare Docs' - condition: and(succeeded(), eq(variables['BuildJob.HAS_DOCS_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_DOCS_CHANGES'], 'true')) inputs: rootFolderOrFile: 'apps/docs/build' includeRootFolder: false @@ -517,17 +517,17 @@ stages: # Upload API artifact as build result - publish: $(Build.ArtifactStagingDirectory)/api-$(Build.BuildId).zip displayName: 'Artifact: Publish API' - condition: and(succeeded(), eq(variables['BuildJob.HAS_BACKEND_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_BACKEND_CHANGES'], 'true')) artifact: api # Upload UI Community artifact as build result - publish: $(Build.ArtifactStagingDirectory)/ui-community-$(Build.BuildId).zip displayName: 'Artifact: Publish UI Community' - condition: and(succeeded(), eq(variables['BuildJob.HAS_FRONTEND_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_FRONTEND_CHANGES'], 'true')) artifact: ui-community # Upload Docs artifact as build result - publish: $(Build.ArtifactStagingDirectory)/docs-$(Build.BuildId).zip displayName: 'Artifact: Publish Docs' - condition: and(succeeded(), eq(variables['BuildJob.HAS_DOCS_CHANGES'], 'true'), ne(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), eq(variables['BuildJob.HAS_DOCS_CHANGES'], 'true')) artifact: docs \ No newline at end of file diff --git a/build-pipeline/core/monorepo-deployment-stage.yml b/build-pipeline/core/monorepo-deployment-stage.yml index 937780a6b..46c0e1bda 100644 --- a/build-pipeline/core/monorepo-deployment-stage.yml +++ b/build-pipeline/core/monorepo-deployment-stage.yml @@ -33,7 +33,7 @@ stages: - stage: ${{parameters.stageName}} displayName: ${{parameters.stageName}} stage dependsOn: Build - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + condition: succeeded() jobs: - template: ../../apps/api/deploy-api.yml parameters: diff --git a/build-pipeline/core/monorepo-edgescan-stage.yml b/build-pipeline/core/monorepo-edgescan-stage.yml index d16e5b315..757fd625b 100644 --- a/build-pipeline/core/monorepo-edgescan-stage.yml +++ b/build-pipeline/core/monorepo-edgescan-stage.yml @@ -14,20 +14,17 @@ stages: - stage: ${{parameters.stageName}} displayName: 'EdgeScan Security Scan' dependsOn: ${{parameters.dependsOn}} - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + condition: succeeded() jobs: - job: EdgeScan displayName: 'EdgeScan CI/CD Integration' - variables: - # Generates a monthly key like "2026-01" to ensure the image is refreshed monthly - cacheMonth: $[format('{0:yyyy-MM}', pipeline.startTime)] pool: vmImage: ${{parameters.vmImageName}} steps: - task: Cache@2 displayName: 'Cache EdgeScan Docker Image' inputs: - key: 'docker | edgescan | latest | $(cacheMonth)' + key: 'docker | edgescan | latest' path: $(Pipeline.Workspace)/docker-cache cacheHitVar: DOCKER_CACHE_HIT diff --git a/knip.json b/knip.json index 998e51308..53aed3022 100644 --- a/knip.json +++ b/knip.json @@ -79,5 +79,5 @@ "@graphql-typed-document-node/core", "ts-scope-trimmer-plugin" ], - "ignoreBinaries": ["func"] + "ignoreBinaries": ["func", "container"] } diff --git a/package.json b/package.json index 206e4c080..43288dea5 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "snyk:iac:report": "snyk iac test iac/build/**/*.json --org=cellixjs --remote-repo-url=https://github.com/CellixJs/cellixjs --target-reference=main --target-name=cellixjs-iac --report", "analyze": "pnpm -r exec -- pnpm dlx @e18e/cli analyze", "prepare": "husky", - "edgescan:run": "docker run --tty --rm edgescan/cicd-integration:latest --api-token $ES_API_TOKEN --asset-id $ES_ASSET_ID --start-scan --max-risk-threshold 3 --wait --color" + "edgescan:agent": "docker run --tty --rm edgescan/cicd-integration:latest --api-token $ES_API_TOKEN --asset-id $ES_ASSET_ID --start-scan --max-risk-threshold 3 --wait --color", + "edgescan:dev": "container run --tty --rm --platform linux/amd64 edgescan/cicd-integration:latest --api-token $ES_API_TOKEN --asset-id 74096 --start-scan --max-risk-threshold 3 --wait --color" }, "devDependencies": { "@amiceli/vitest-cucumber": "^5.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4673ed292..b9c500849 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2139,6 +2139,10 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -11920,7 +11924,7 @@ snapshots: dependencies: '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 chalk: 4.1.2 fb-watchman: 2.0.2 graphql: 16.12.0 @@ -12909,6 +12913,8 @@ snapshots: '@babel/runtime@7.28.4': {} + '@babel/runtime@7.28.6': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -15644,7 +15650,7 @@ snapshots: '@rc-component/async-validator@5.0.4': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@rc-component/color-picker@2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: @@ -15657,14 +15663,14 @@ snapshots: '@rc-component/context@1.4.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 rc-util: 5.44.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) '@rc-component/mini-decimal@1.1.0': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@rc-component/mutate-observer@1.1.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: @@ -15676,7 +15682,7 @@ snapshots: '@rc-component/portal@1.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 classnames: 2.5.1 rc-util: 5.44.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 @@ -15952,7 +15958,7 @@ snapshots: '@slorber/react-helmet-async@1.3.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 invariant: 2.2.4 prop-types: 15.8.1 react: 19.2.0 @@ -16272,7 +16278,7 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -19180,7 +19186,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -22013,7 +22019,7 @@ snapshots: rc-overflow@1.5.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 classnames: 2.5.1 rc-resize-observer: 1.4.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0) rc-util: 5.44.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -22192,7 +22198,7 @@ snapshots: rc-virtual-list@3.19.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 classnames: 2.5.1 rc-resize-observer: 1.4.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0) rc-util: 5.44.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -22474,7 +22480,7 @@ snapshots: relay-runtime@12.0.0: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 fbjs: 3.0.5 invariant: 2.2.4 transitivePeerDependencies: