Maleckidd/CypressExample-OWASPJuiceShop
GitHub: Maleckidd/CypressExample-OWASPJuiceShop
该项目是一个使用 Cypress 对 OWASP Juice Shop 进行端到端功能测试和安全挑战自动化求解的测试套件,同时记录了向 Playwright 迁移的完整过程。
Stars: 0 | Forks: 0
# CypressExample-OWASPJuiceShop
  
## 🎯 问题描述
OWASP Juice Shop 是一个故意设计成包含漏洞的电子商务应用,用于学习安全测试。我不满足于仅仅进行“正常路径”的点击操作:
- 在真实、动态的 Angular 应用程序上,对典型用户旅程(注册、登录、购物车、联系)进行**完整的 E2E 覆盖**。
- **自动化挑战求解** — 通过编程方式执行攻击(XSS、XXE、IDOR、API manipulation)并验证 Juice Shop 是否确实将其标记为已解决。
- **驯化动态 UI** — 该应用程序会在每次新会话时弹出欢迎弹窗和 Cookie 横幅,如果天真地处理它们,会破坏测试。
挑战不在于“写几个测试”——而在于构建一个测试套件,该套件能够在极其动态的 UI 上干净地运行,并且能够验证攻击的**效果**,而不仅仅是确认攻击被触发。
## 🛠️ 我的实现方法
**Page Object Model 架构。** 所有 UI 交互逻辑都位于 `POM/` 中(9 个对象:`loginPage`、`registrationPage`、`headerBar`、`products`、`orders`、`contactPage`、`complaint`、`sideNav`、`FirstVisitPopups`)。`JuiceShopTests/` 中的测试描述了测试的*内容*;而 POM 知道*如何*测试。
**挑战代码化。** Attack payload 被提取到 `fixtures/challengesPayloads.json` 以及 `xxe.xml` / `test1.xml` 文件中。测试按难度分组:
| 测试套件 | 场景数 | 范围 |
|-------|:---------:|-------|
| `1StarChallenges.cy.js` | 16 | Score Board、Bonus Payload、Confidential Document、Error Handling、Exposed Metrics… |
| `2StarsChallenges.cy.js` | 17 | 中等难度的挑战 |
| `3StarsChallenges.cy.js` | 23 | 高级攻击(XXE、IDOR、API manipulation) |
| `homePage.cy.js` | 8 | 基础 UI 操作 — 参考资料 |
| `recurseExamples.spec .cy.js` | 2 | 使用 `cypress-recurse` 的重试模式 |
**通过网络而非 UI 进行验证。** 一个关键决策:自定义命令 `cy.checkIsAchivSolvedXHR()` 通过 API 响应检查挑战状态,而不是依赖于 UI 通知。这种方式不受动画和消失的 toast 影响。
**配置与测试分离。** 环境变量保存在 fixtures(`JuiceShop-dev.json` / `JuiceShop-stag.json`)中,并通过 `--env JuiceShop_ENV` 标志进行选择,因此 URL 和凭证不会被硬编码到测试用例中。实际上,所有测试都是针对运行在 `http://localhost:3000/` 的单个本地 Juice Shop 实例进行的。
## ⚠️ 遇到的问题
坦诚面对局限性——因为它们推动了后续的发展:
- **DOM XSS 在框架级别被阻止。** 测试 `it.skip('3 - DOM XSS')` — 触发 XSS 后,会弹出一个 Cypress 无法关闭的原生 `alert()`,导致超时。挑战*确实*被解开了,但测试无法干净地完成。保留为 `skip` 状态并添加注释,而不是假装它通过了。
- **对抗欢迎和 Cookie 弹窗。** 需要一个专门的 `FirstVisitPopups.pom` 以及测试之间的手动 Cookie 保留(`Cypress.Cookies.preserveOnce`)——这种方式很脆弱且依赖于执行顺序。
- **单线程运行架构。** Cypress 在浏览器的事件循环中运行——某些攻击(那些依赖原生对话框或多个标签页的攻击)很难或根本无法自动化。
## 🚀 结果:AI 辅助迁移至 Playwright
Cypress 的局限性成为了迁移到 **[PlaywrightExample-OWASPJuiceShop][pw]** 的理由——该迁移在 AI 的协助下完成,并已完整记录在案。
**发生的变化:**
| | Cypress(之前) | Playwright(之后) |
|---|---|---|
| 框架 | Cypress 10.2.0 | Playwright 1.58.2 |
| 语言 | JavaScript (ES6) | **TypeScript**(带有类型的 POM) |
| 选择器 | `cy.get('[id=email]')` | web-first **Locators** |
| Page Objects | 9 个 `.pom.js` 文件 | 7 个类 + `BasePage` |
| 测试数据 | `faker` | 自定义 `testDataGenerator` |
| 浏览器 | Chrome | Chromium + Firefox,并行运行 |
| 等待 | 显式 `wait`/重试 | 内置 auto-waiting |
**迁移成果:**
- ✅ **强类型代码** — 选择器和方法错误在编译时捕获,而不是在运行时。
- ✅ **Web-first 断言** — 不再需要手动管理 Cookie 或脆弱的 `wait`。
- ✅ **开箱即用的跨浏览器和并行测试**。
- ✅ **完整文档化的过程** — [`MIGRATION_GUIDE.md`][pw]、`MIGRATION_SUMMARY.md` 和 `QUICK_REFERENCE.md`,其中包含每个 POM 和模式的 1:1 映射。
## 📦 设置
需要 [Cypress][cypress] 和一个正在运行的 [OWASP Juice Shop](https://github.com/bkimminich/juice-shop) 实例。
```
git clone https://github.com/Maleckidd/CypressExample-OWASPJuiceShop.git
cd CypressExample-OWASPJuiceShop
npm i
```
**运行测试:**
```
npm run cypress:open # interactive mode (env: stag)
npm run cypress:headless # headless in Chrome (env: stag)
npm run cypress:open:dev # interactive mode (env: dev)
```
Juice Shop 默认运行在 `http://localhost:3000/`(即 `cypress.config.js` 中的 `baseUrl`)。
## 🗂️ 结构
```
CypressExample-OWASPJuiceShop/
├── JuiceShopTests/ # test suites (challenges 1–3⭐ + homePage)
├── POM/ # Page Object Model (9 objects)
├── fixtures/ # attack payloads, env data, XML files
├── support/ # custom commands (checkIsAchivSolvedXHR)
├── plugins/ # node events config
└── cypress.config.js # baseUrl, specPattern, supportFile
```
标签:CISA项目, CMS安全, Cypress, E2E测试, JavaScript, 安全测试, 攻击性安全, 数据可视化, 特征检测, 自动化攻防, 自定义脚本