Kunal-Murarka/powerapps-codeapps-pac-scan
GitHub: Kunal-Murarka/powerapps-codeapps-pac-scan
面向 Power Apps Code Apps 的构建时安全扫描器,在开发阶段离线检测 DLP 违规、CSP 违规、硬编码凭据、不安全网络请求和依赖项漏洞,避免策略冲突在运行时才以静默故障暴露。
Stars: 0 | Forks: 0
# pac-scan
**[Power Apps Code Apps](https://learn.microsoft.com/en-us/power-apps/developer/code-apps/overview) 的构建时安全扫描器**(React / Vue + Vite)。
pac-scan 能够在 DLP 违规、CSP 违规、硬编码的秘密信息、不安全的 fetch 调用以及易受攻击的依赖项**演变成 Power Apps Player 沙箱中静默的运行时故障之前**将它们捕获。
## 什么是 Code Apps?
[Power Apps Code Apps](https://learn.microsoft.com/en-us/power-apps/developer/code-apps/overview) 允许开发者将 Power Apps 的功能引入到在代码优先 IDE 中构建的自定义 Web 应用中。你可以使用 React 或 Vue 等框架在本地进行构建,然后在 Power Platform 中发布并托管该应用——它将在平台的**托管策略**下运行:DLP、CSP、条件访问和连接器权限。
由于这些策略是由 Power Platform 管理员(而非开发者)设置的,因此违规行为在开发期间是静默的,只有在 Power Apps Player 沙箱内部运行时才会以故障的形式暴露出来。pac-scan 通过拉取实时策略的快照并在**构建时完全离线**运行所有检查来弥补这一差距。
### pac-scan 能捕获的问题一览
| 规则 | 检测内容 | 严重性 | 漏检后果 |
|---|---|---|---|
| **PAC001** | 硬编码的秘密信息(API 密钥、token、GUID) | CRITICAL | 缓存包中的凭据泄露 |
| **PAC002** | 对未加入允许列表的域的 `fetch()` / XHR 调用 | HIGH | 运行时出现静默网络故障 |
| **PAC003** | 受阻或非业务连接器的使用 | CRITICAL | 应用崩溃且无回退机制 |
| **PAC004** | `eval()`、`innerHTML`、宽松的 CSP 头 | HIGH | Player 沙箱内出现 CSP 拒绝 |
| **PAC005** | npm audit 严重/高危公告 | CRITICAL | 生产包中存在可被利用的依赖项 |
## 前置条件
| 需求 | 说明 |
|---|---|
| **Node.js 18+ (LTS)** | [下载](https://nodejs.org/) |
| **pac CLI** | 仅 `pac-scan fetch` 需要。安装命令:`npm install -g @microsoft/powerplatform-cli` · [文档](https://learn.microsoft.com/power-platform/developer/cli/introduction) |
| **已启用 Code Apps** | Power Platform 管理员必须在目标环境中启用 Code Apps:管理中心 → 环境 → 选择环境 → 设置 → 功能 → **启用代码应用**。 |
| **Power Apps 高级版许可证** | 运行代码应用的最终用户需要拥有 [Power Apps 高级版许可证](https://www.microsoft.com/power-platform/products/power-apps/pricing)。 |
## 为什么需要它
Power Apps Code Apps 运行在 Power Apps Player 内部,这是一个受两层策略严格管理的紧密沙箱 iframe:
| 层级 | 执行内容 |
|---|---|
| **DLP 策略** | 应用可以使用哪些连接器。管理员可以随时阻止某个连接器——你的应用将停止工作,且没有任何构建时的警告。 |
| **CSP 指令** | `fetch()` / XHR 可以访问哪些域,允许哪些脚本来源,以及哪些域可以嵌入播放器。 |
这两层均由 Power Platform 管理员配置,而非开发者。
**Solution Checker**——标准的 Power Platform 静态分析工具——涵盖了画布应用、模型驱动应用和 Power Automate 流。然而,**它并不分析 Code Apps 的源代码**,因为 Code Apps 是在平台的低代码创作界面之外构建的自定义 JavaScript/TypeScript 包。
pac-scan 填补了这一空白:它只需拉取一次实时策略的快照,随后的每次扫描都会完全离线运行——无需网络,无遥测,也没有数据流出边界。
## 快速开始
```
# 1. 安装 pac CLI(一次性操作 — 如已安装请跳过)
npm install -g @microsoft/powerplatform-cli
# 2. 认证 pac CLI(一次性操作 — 打开浏览器登录)
pac auth create
# 按照浏览器提示使用您的 Power Platform 账户登录。
# 验证:pac auth list(查找标有 * 表示当前活跃的条目)
# 3. 安装 pac-scan
npm install --save-dev github:Kunal-Murarka/powerapps-codeapps-pac-scan
```
接下来,在你的项目根目录中创建 `pac-scan.config.yaml`(完整参考请参见[配置](#configuration-pac-scanconfigyaml))。最小示例:
```
default_environment: prod
environments:
prod:
dlp_snapshot: .pac-scan/current/prod.json
csp_snapshot: .pac-scan/current/prod.json
environment_url: https://your-org.crm.dynamics.com/
fail_on_severity:
prod: MEDIUM
scan_paths: [src]
scan_extensions: [.ts, .tsx, .js, .jsx]
```
```
# 4. 拉取实时策略(写入 .pac-scan/current/prod.json)
npx pac-scan fetch --env prod
# 5. 扫描
npx pac-scan run --env prod
# 不确定一切是否设置正确?先运行验证器:
npx pac-scan validate
```
## CLI 命令
### `pac-scan fetch`
从 Power Platform 拉取实时的 DLP 和 CSP 策略,并将快照写入 `.pac-scan/current/.json`。
```
pac-scan fetch --env prod
pac-scan fetch --env dev --environment-id 00000000-0000-0000-0000-000000000000
pac-scan fetch --env uat --verbose
```
选项:
| 标志 | 默认值 | 描述 |
|---|---|---|
| `--env ` | 配置中的 `default_environment` | 目标环境名称 |
| `--environment-id ` | 自动检测 | 覆盖 Power Platform 环境 GUID |
| `--config ` | 自动检测 | `pac-scan.config.yaml` 的路径 |
| `--verbose` | false | 打印详细的拉取进度 |
需要 pac CLI 已安装并完成身份验证:
```
npm install -g @microsoft/powerplatform-cli
pac auth create # opens a browser — sign in with your Power Platform account
```
### `pac-scan run`
根据保存的快照扫描源文件。完全离线运行。
```
pac-scan run --env prod
pac-scan run --env dev --fail-on CRITICAL
pac-scan run --env uat --output ./reports --format json
```
选项:
| 标志 | 默认值 | 描述 |
|---|---|---|
| `--env ` | 配置中的 `default_environment` | 目标环境 |
| `--fail-on ` | 配置中的 `fail_on_severity` | 覆盖失败阈值 |
| `--path ` | `cwd` | 要扫描的根目录 |
| `--output ` | `cwd` | 写入 JSON 报告的目录 |
| `--format terminal\|json` | `terminal` | 输出格式 |
| `--verbose` | false | 扫描时打印每个文件 |
退出代码 0 = 通过,退出代码 1 = 失败或错误。
### `pac-scan diff`
比较同一环境的两个快照,显示日期之间的策略变化。适用于解答“为什么在管理员更改 DLP 策略后,我的扫描开始失败了?”
```
pac-scan diff --env prod --from 2026-05-01 --to 2026-05-05
pac-scan diff --env prod --from 2026-05-01 --to 2026-05-05 --format json
```
选项:
| 标志 | 默认值 | 描述 |
|---|---|---|
| `--env ` | 必填 | 要比较的环境 |
| `--from ` | 必填 | 基准日期 (`YYYY-MM-DD`) —— 查找最近的快照 |
| `--to ` | 必填 | 目标日期 (`YYYY-MM-DD`) —— 查找最近的快照 |
| `--format terminal\|json` | `terminal` | 输出格式 |
| `--config ` | 自动检测 | 配置文件路径 |
### `pac-scan policy`
显示当前策略快照的格式化视图——DLP 策略、连接器风险层级、端点过滤器 URL 和 CSP 指令。
```
pac-scan policy --env prod
pac-scan policy --env dev --format json
```
选项:
| 标志 | 默认值 | 描述 |
|---|---|---|
| `--env ` | 配置中的 `default_environment` | 目标环境 |
| `--format terminal\|json` | `terminal` | 输出格式 |
| `--config ` | 自动检测 | 配置文件路径 |
### `pac-scan validate`
检查你的设置是否完整且正确。在建立新项目或调试意外故障时,请首先运行此命令。
```
pac-scan validate
pac-scan validate --env dev
```
检查项:
- 配置文件存在且语法有效
- 所有配置的快照文件均存在于磁盘上
- pac CLI 已安装并在 PATH 中
- pac CLI 至少有一个已认证的配置文件
## 配置:`pac-scan.config.yaml`
将此文件放置在你的项目根目录中(pac-scan 会从 `cwd` 向上搜索以找到它):
```
default_environment: prod
environments:
dev:
dlp_snapshot: .pac-scan/current/dev.json
csp_snapshot: .pac-scan/current/dev.json
environment_url: https://org-dev.crm.dynamics.com/
# environment_id: 00000000-0000-0000-0000-000000000000 # optional: skip pac org list lookup
uat:
dlp_snapshot: .pac-scan/current/uat.json
csp_snapshot: .pac-scan/current/uat.json
environment_url: https://org-uat.crm.dynamics.com/
prod:
dlp_snapshot: .pac-scan/current/prod.json
csp_snapshot: .pac-scan/current/prod.json
environment_url: https://org.crm.dynamics.com/
# 按环境划分的导致构建失败的最低严重级别
fail_on_severity:
dev: CRITICAL
uat: HIGH
prod: MEDIUM
# 要扫描的源文件
scan_paths: [src]
scan_extensions: [.ts, .tsx, .js, .jsx]
```
## Git hook
当任何发现项达到 `dev` 阈值(默认为 CRITICAL)时,阻止提交。
```
# 安装(跨平台,覆盖前会提示)
npm run install-hooks
# 或在 Unix/macOS 上手动执行
bash scripts/install-hooks.sh
# 或在 Windows (PowerShell) 上
.\scripts\install-hooks.ps1
```
在紧急情况下跳过检查:`git commit --no-verify`
## Azure DevOps 流水线
完整的 4 阶段流水线请参见 [examples/azure-devops-pipeline.yml](examples/azure-devops-pipeline.yml)。
快速参考——将以下两个步骤添加到任何现有流水线中:
```
- script: npm install -g pac-scan
displayName: 'Install pac-scan'
- task: PACSecurityScan@1
displayName: 'Run Security Scan'
inputs:
command: run
environment: prod
failOnSeverity: HIGH
outputPath: $(Build.ArtifactStagingDirectory)/scan-report.json
```
**将 `PACSecurityScan@1` 任务发布到你的 Azure DevOps 组织:**
```
# 1. 安装扩展打包工具
npm install -g tfx-cli
# 2. 构建任务
cd azure-devops-task
npm install
npm run build
# 3. 在 https://marketplace.visualstudio.com/manage/publishers 创建发布者
# 然后在下方将 PUBLISHER 设置为您的发布者 ID
# 4. 打包扩展(创建一个 .vsix 文件)
tfx extension create --manifest-globs vss-extension.json
# 5. 发布(或通过 Marketplace 门户手动上传 .vsix)
tfx extension publish --publisher
```
或者,将 `azure-devops-task/` 文件夹直接复制到你的流水线仓库中,并通过将 `task.json` 的 `execution.Node20.target` 设置为正确的相对路径,将其作为本地任务引用。
## GitHub Actions
完整的 5 作业工作流请参见 [examples/github-actions-workflow.yml](examples/github-actions-workflow.yml)。
快速参考——将此步骤添加到任何作业中:
```
- uses: your-org/pac-scan-action@v1
id: scan
with:
command: run
environment: prod
fail-on-severity: HIGH
output-path: ./pac-scan-report.json
```
**发布该 Action 以便工作流可以引用它:**
```
# 1. 构建该 action
cd github-action
npm install
npm run build
# 2. 为该 action 创建一个新的 GitHub 仓库
# 例如:your-org/pac-scan-action
gh repo create your-org/pac-scan-action --public
# 3. 复制 action 文件到该仓库并 push
cp -r github-action/. /path/to/pac-scan-action/
cd /path/to/pac-scan-action
git init && git add -A
git commit -m "feat: initial release v1"
git tag v1
git remote add origin https://github.com/your-org/pac-scan-action.git
git push origin main --tags
```
然后在工作流中引用它:`uses: your-org/pac-scan-action@v1`。
## 5 项规则
### PAC001 — 硬编码的秘密信息 (CRITICAL)
检测源文件中硬编码的 API 密钥、Bearer token、客户端密码、租户/应用程序 GUID、Azure 存储账户密钥和私钥头。
**重要性:** Power Apps Player 可能会将应用包缓存在浏览器存储中,并且 Code Apps 有时会被部署到共享租户中。泄露的密钥就是泄露的密钥。
**排除项:** 测试文件(`*.test.ts`、`*.spec.ts`)、引用环境变量的代码行(`process.env.`、`import.meta.env.`、`EnvVar(`、`getEnvironmentVariable(`)以及注释行不会被标记。
**修复方法:** 使用 `@microsoft/powerplatform-envvar` 在运行时读取环境变量。切勿将凭据放入源代码中。
### PAC002 — 对未加入允许列表的域的原始 fetch 请求 (HIGH / MEDIUM)
检测指向不在快照的 `connect_src` 列表或连接器端点过滤器中的域的 `fetch()`、`axios.*()` 和 `XMLHttpRequest.open()` 调用。
| 严重性 | 触发条件 |
|---|---|
| HIGH | 静态 URL,明确不在允许列表中 |
| MEDIUM | 动态 URL(带有插值的变量或模板字面量) |
**重要性:** Power Apps Player CSP 会阻止对不在 `connect-src` 中的任何域的 `fetch()` 调用。该调用会在运行时以网络错误静默失败——这不是一个有用的错误提示。
**修复方法:** 将该域添加到 Power Platform 管理中心的 DLP 策略端点过滤器中,然后运行 `pac-scan fetch` 刷新快照。
### PAC003 — DLP 连接器违规 (CRITICAL / HIGH / MEDIUM)
检测在环境的 DLP 策略中被分类为已阻止或非业务的连接器的导入、`connectorName:` 赋值和 `useConnector()` 调用。
| 严重性 | 分类 |
|---|---|
| CRITICAL | 连接器在**已阻止**列表中 |
| HIGH | 连接器在**非业务**列表中 |
| MEDIUM | 在快照中完全找不到该连接器 |
**重要性:** 被阻止的连接器调用会在 Power Apps Player 运行时引发错误。且没有任何回退机制。
### PAC004 — CSP 违规 (HIGH / MEDIUM)
检测 Power Apps Player CSP 会拒绝的模式:
- `eval()`、`new Function(string)`、`setTimeout(string, ...)` —— 违反 `script-src`
- 包含 `