@codyswann/lisa 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cdk/copy-overwrite/jest.cdk.ts +71 -0
- package/cdk/copy-overwrite/jest.config.ts +28 -0
- package/cdk/copy-overwrite/tsconfig.cdk.json +14 -0
- package/cdk/copy-overwrite/tsconfig.eslint.json +2 -1
- package/cdk/copy-overwrite/tsconfig.json +3 -0
- package/cdk/create-only/cdk.json +23 -0
- package/cdk/create-only/tsconfig.local.json +4 -0
- package/expo/copy-overwrite/.claude/skills/owasp-zap/SKILL.md +56 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +5 -10
- package/expo/copy-overwrite/.github/workflows/zap-baseline.yml +107 -0
- package/expo/copy-overwrite/.zap/baseline.conf +36 -0
- package/expo/copy-overwrite/jest.config.ts +28 -0
- package/expo/copy-overwrite/jest.expo.ts +119 -0
- package/expo/copy-overwrite/scripts/zap-baseline.sh +92 -0
- package/expo/copy-overwrite/tsconfig.eslint.json +2 -1
- package/expo/copy-overwrite/tsconfig.expo.json +12 -0
- package/expo/copy-overwrite/tsconfig.json +5 -0
- package/expo/create-only/.github/workflows/ci.yml +8 -0
- package/expo/create-only/tsconfig.local.json +3 -0
- package/expo/package-lisa/package.lisa.json +4 -2
- package/nestjs/copy-overwrite/.github/workflows/zap-baseline.yml +123 -0
- package/nestjs/copy-overwrite/.zap/baseline.conf +39 -0
- package/nestjs/copy-overwrite/jest.config.ts +28 -0
- package/nestjs/copy-overwrite/jest.nestjs.ts +89 -0
- package/nestjs/copy-overwrite/scripts/zap-baseline.sh +99 -0
- package/nestjs/copy-overwrite/tsconfig.build.json +5 -0
- package/nestjs/copy-overwrite/tsconfig.eslint.json +10 -0
- package/nestjs/copy-overwrite/tsconfig.json +5 -0
- package/nestjs/copy-overwrite/tsconfig.nestjs.json +16 -0
- package/nestjs/copy-overwrite/tsconfig.spec.json +7 -0
- package/nestjs/create-only/.github/workflows/ci.yml +8 -0
- package/nestjs/create-only/tsconfig.local.json +6 -0
- package/nestjs/package-lisa/package.lisa.json +2 -1
- package/package.json +1 -1
- package/typescript/copy-overwrite/.claude/commands/security/zap-scan.md +12 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +100 -5
- package/typescript/copy-overwrite/jest.base.ts +112 -0
- package/typescript/copy-overwrite/jest.config.ts +34 -0
- package/typescript/copy-overwrite/jest.typescript.ts +72 -0
- package/typescript/copy-overwrite/tsconfig.base.json +15 -0
- package/typescript/copy-overwrite/tsconfig.eslint.json +3 -2
- package/typescript/copy-overwrite/tsconfig.json +5 -0
- package/typescript/copy-overwrite/tsconfig.typescript.json +11 -0
- package/typescript/create-only/jest.config.local.ts +30 -0
- package/typescript/create-only/jest.thresholds.json +8 -0
- package/typescript/create-only/tsconfig.local.json +6 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is managed by Lisa.
|
|
3
|
+
* Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Jest Configuration - CDK Stack
|
|
8
|
+
*
|
|
9
|
+
* Provides AWS CDK-specific Jest configuration targeting
|
|
10
|
+
* the test/ directory with spec and integration-spec patterns.
|
|
11
|
+
*
|
|
12
|
+
* Inheritance chain:
|
|
13
|
+
* jest.cdk.ts (this file)
|
|
14
|
+
* └── jest.base.ts
|
|
15
|
+
*
|
|
16
|
+
* @see https://jestjs.io/docs/configuration
|
|
17
|
+
* @module jest.cdk
|
|
18
|
+
*/
|
|
19
|
+
import type { Config } from "jest";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
defaultCoverageExclusions,
|
|
23
|
+
defaultThresholds,
|
|
24
|
+
mergeConfigs,
|
|
25
|
+
mergeThresholds,
|
|
26
|
+
} from "./jest.base.ts";
|
|
27
|
+
|
|
28
|
+
// Re-export base utilities for entry-point configs
|
|
29
|
+
export {
|
|
30
|
+
defaultCoverageExclusions,
|
|
31
|
+
defaultThresholds,
|
|
32
|
+
mergeConfigs,
|
|
33
|
+
mergeThresholds,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for configuring the CDK Jest config factory.
|
|
38
|
+
*/
|
|
39
|
+
interface CdkJestOptions {
|
|
40
|
+
/** Coverage thresholds (merged defaults + project overrides) */
|
|
41
|
+
readonly thresholds?: Config["coverageThreshold"];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates a Jest configuration for AWS CDK projects.
|
|
46
|
+
*
|
|
47
|
+
* @param options - Configuration options for threshold overrides
|
|
48
|
+
* @param options.thresholds - Coverage thresholds (merged defaults + project overrides)
|
|
49
|
+
* @returns Jest config object with ts-jest transform, node environment, and CDK-specific paths
|
|
50
|
+
* @remarks CDK projects typically use CommonJS modules and keep tests in a
|
|
51
|
+
* separate test/ directory. Coverage is collected only from lib/ and util/
|
|
52
|
+
* directories since bin/ contains entry-point code with minimal logic.
|
|
53
|
+
*/
|
|
54
|
+
export const getCdkJestConfig = ({
|
|
55
|
+
thresholds = defaultThresholds,
|
|
56
|
+
}: CdkJestOptions = {}): Config => ({
|
|
57
|
+
testEnvironment: "node",
|
|
58
|
+
roots: ["<rootDir>/test"],
|
|
59
|
+
testRegex: "(.*\\.(spec|integration-spec)\\.ts)$",
|
|
60
|
+
transform: {
|
|
61
|
+
"^.+\\.ts$": "ts-jest",
|
|
62
|
+
},
|
|
63
|
+
moduleFileExtensions: ["js", "json", "ts"],
|
|
64
|
+
collectCoverageFrom: [
|
|
65
|
+
"lib/**/*.ts",
|
|
66
|
+
"util/**/*.ts",
|
|
67
|
+
...defaultCoverageExclusions,
|
|
68
|
+
],
|
|
69
|
+
coverageThreshold: thresholds,
|
|
70
|
+
testTimeout: 10000,
|
|
71
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is managed by Lisa.
|
|
3
|
+
* Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Jest Configuration - Main Entry Point (CDK)
|
|
8
|
+
*
|
|
9
|
+
* Imports the CDK-specific configuration and project-local customizations.
|
|
10
|
+
* Do not modify this file directly - use jest.config.local.ts for project-specific settings.
|
|
11
|
+
*
|
|
12
|
+
* Inheritance chain:
|
|
13
|
+
* jest.config.ts (this file)
|
|
14
|
+
* └── jest.cdk.ts
|
|
15
|
+
* └── jest.base.ts
|
|
16
|
+
*
|
|
17
|
+
* @see https://jestjs.io/docs/configuration
|
|
18
|
+
* @module jest.config
|
|
19
|
+
*/
|
|
20
|
+
import { mergeConfigs, mergeThresholds } from "./jest.base.ts";
|
|
21
|
+
import { defaultThresholds, getCdkJestConfig } from "./jest.cdk.ts";
|
|
22
|
+
|
|
23
|
+
import localConfig from "./jest.config.local.ts";
|
|
24
|
+
import thresholdsOverrides from "./jest.thresholds.json" with { type: "json" };
|
|
25
|
+
|
|
26
|
+
const thresholds = mergeThresholds(defaultThresholds, thresholdsOverrides);
|
|
27
|
+
|
|
28
|
+
export default mergeConfigs(getCdkJestConfig({ thresholds }), localConfig);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"lib": ["es2020"],
|
|
7
|
+
"inlineSourceMap": true,
|
|
8
|
+
"inlineSources": true,
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"strictPropertyInitialization": false,
|
|
11
|
+
"noUnusedLocals": false,
|
|
12
|
+
"noUnusedParameters": false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"rootDir": ".",
|
|
5
5
|
"noEmit": true,
|
|
6
|
+
"allowImportingTsExtensions": true,
|
|
6
7
|
"module": "NodeNext",
|
|
7
8
|
"moduleResolution": "NodeNext"
|
|
8
9
|
},
|
|
9
|
-
"include": ["lib/**/*", "bin/**/*", "test/**/*", "*.config.ts", "eslint.*.ts"],
|
|
10
|
+
"include": ["lib/**/*", "bin/**/*", "test/**/*", "*.config.ts", "eslint.*.ts", "jest.*.ts"],
|
|
10
11
|
"exclude": ["node_modules", "dist", "cdk.out"]
|
|
11
12
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"app": "npx tsx bin/infrastructure.ts",
|
|
3
|
+
"watch": {
|
|
4
|
+
"include": ["**"],
|
|
5
|
+
"exclude": [
|
|
6
|
+
"README.md",
|
|
7
|
+
"cdk*.json",
|
|
8
|
+
"**/*.d.ts",
|
|
9
|
+
"**/*.js",
|
|
10
|
+
"tsconfig.json",
|
|
11
|
+
"package.json",
|
|
12
|
+
"yarn.lock",
|
|
13
|
+
"node_modules",
|
|
14
|
+
"test"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"context": {
|
|
18
|
+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
|
19
|
+
"@aws-cdk/core:checkSecretUsage": true,
|
|
20
|
+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
|
|
21
|
+
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# OWASP ZAP Baseline Scanning
|
|
2
|
+
|
|
3
|
+
OWASP ZAP (Zed Attack Proxy) performs DAST (Dynamic Application Security Testing) by scanning a running application for common security vulnerabilities from the OWASP Top 10.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- After making changes to HTTP headers, authentication, or security middleware
|
|
8
|
+
- Before deploying to staging or production
|
|
9
|
+
- When reviewing security scan results from CI
|
|
10
|
+
- When triaging ZAP findings from pull request checks
|
|
11
|
+
|
|
12
|
+
## Running Locally
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Requires Docker to be installed and running
|
|
16
|
+
bash scripts/zap-baseline.sh
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The scan builds the Expo web export, serves it locally, and runs ZAP against it. Reports are saved to `zap-report.html`, `zap-report.json`, and `zap-report.md`.
|
|
20
|
+
|
|
21
|
+
## Interpreting Results
|
|
22
|
+
|
|
23
|
+
ZAP findings are categorized by risk level:
|
|
24
|
+
|
|
25
|
+
| Risk | Action |
|
|
26
|
+
|------|--------|
|
|
27
|
+
| **High** | Fix immediately — indicates exploitable vulnerability |
|
|
28
|
+
| **Medium** | Fix before deployment — security best practice violation |
|
|
29
|
+
| **Low** | Fix when convenient — minor security improvement |
|
|
30
|
+
| **Informational** | Review — may be false positive or acceptable risk |
|
|
31
|
+
|
|
32
|
+
## Common Findings and Fixes
|
|
33
|
+
|
|
34
|
+
### Infrastructure-Level (fix at CDN/hosting, not in code)
|
|
35
|
+
|
|
36
|
+
- **CSP Header Not Set**: Configure Content-Security-Policy at CDN or hosting platform. Expo web exports need `script-src 'self' 'unsafe-inline'` for hydration.
|
|
37
|
+
- **HSTS Not Set**: Configure Strict-Transport-Security at CDN/load balancer.
|
|
38
|
+
- **X-Frame-Options**: Use `frame-ancestors` in CSP at CDN level.
|
|
39
|
+
|
|
40
|
+
### Application-Level (fix in code)
|
|
41
|
+
|
|
42
|
+
- **Cookie flags missing**: Ensure all cookies set `HttpOnly`, `Secure`, and `SameSite` attributes.
|
|
43
|
+
- **Debug error messages**: Ensure error boundaries don't leak stack traces in production.
|
|
44
|
+
- **Server version disclosure**: Remove or mask the `Server` response header.
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
ZAP scan rules are configured in `.zap/baseline.conf`. Each line controls how ZAP treats a specific rule:
|
|
49
|
+
|
|
50
|
+
- `IGNORE`: Skip the rule entirely
|
|
51
|
+
- `WARN`: Report finding but don't fail the build
|
|
52
|
+
- `FAIL`: Fail the build if this finding is detected
|
|
53
|
+
|
|
54
|
+
## CI Integration
|
|
55
|
+
|
|
56
|
+
ZAP runs automatically in CI via the `zap-baseline.yml` workflow. Results are uploaded as artifacts and the build fails on medium+ severity findings.
|
|
@@ -190,17 +190,12 @@ test("sets isSubmitting to true", () => {});
|
|
|
190
190
|
|
|
191
191
|
## Jest Configuration
|
|
192
192
|
|
|
193
|
-
###
|
|
193
|
+
### Manual React Native Resolution (No Preset)
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"jest": {
|
|
200
|
-
"preset": "jest-expo/universal"
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
```
|
|
195
|
+
Lisa configures Jest manually instead of using the `jest-expo` preset to avoid
|
|
196
|
+
jsdom incompatibility with `react-native/jest/setup.js`. The configuration in
|
|
197
|
+
`jest.expo.ts` provides haste, resolver, transform, and setupFiles that match
|
|
198
|
+
the preset's behavior without redefining `window`.
|
|
204
199
|
|
|
205
200
|
### Use Fake Timers with userEvent
|
|
206
201
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# This file is managed by Lisa.
|
|
2
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
3
|
+
|
|
4
|
+
name: ZAP Baseline Scan (Expo)
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_call:
|
|
8
|
+
inputs:
|
|
9
|
+
node_version:
|
|
10
|
+
description: 'Node.js version to use'
|
|
11
|
+
required: false
|
|
12
|
+
default: '22.21.1'
|
|
13
|
+
type: string
|
|
14
|
+
package_manager:
|
|
15
|
+
description: 'Package manager to use (npm, yarn, or bun)'
|
|
16
|
+
required: false
|
|
17
|
+
default: 'bun'
|
|
18
|
+
type: string
|
|
19
|
+
zap_target_url:
|
|
20
|
+
description: 'Override URL for ZAP to scan (default: http://localhost:3000)'
|
|
21
|
+
required: false
|
|
22
|
+
default: 'http://localhost:3000'
|
|
23
|
+
type: string
|
|
24
|
+
zap_rules_file:
|
|
25
|
+
description: 'Path to ZAP rules configuration file'
|
|
26
|
+
required: false
|
|
27
|
+
default: '.zap/baseline.conf'
|
|
28
|
+
type: string
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
zap_baseline:
|
|
32
|
+
name: ZAP Baseline Scan
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
timeout-minutes: 20
|
|
35
|
+
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout repository
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- name: Setup Node.js
|
|
41
|
+
uses: actions/setup-node@v4
|
|
42
|
+
with:
|
|
43
|
+
node-version: ${{ inputs.node_version }}
|
|
44
|
+
cache: ${{ inputs.package_manager != 'bun' && inputs.package_manager || '' }}
|
|
45
|
+
|
|
46
|
+
- name: Setup Bun
|
|
47
|
+
if: inputs.package_manager == 'bun'
|
|
48
|
+
uses: oven-sh/setup-bun@v2
|
|
49
|
+
with:
|
|
50
|
+
bun-version: '1.3.8'
|
|
51
|
+
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: |
|
|
54
|
+
if [ "${{ inputs.package_manager }}" = "npm" ]; then
|
|
55
|
+
npm ci
|
|
56
|
+
elif [ "${{ inputs.package_manager }}" = "yarn" ]; then
|
|
57
|
+
yarn install --frozen-lockfile
|
|
58
|
+
elif [ "${{ inputs.package_manager }}" = "bun" ]; then
|
|
59
|
+
bun install --frozen-lockfile
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
- name: Build web export
|
|
63
|
+
run: npx expo export --platform web
|
|
64
|
+
|
|
65
|
+
- name: Start static server
|
|
66
|
+
run: |
|
|
67
|
+
npx serve dist -l 3000 &
|
|
68
|
+
SERVER_PID=$!
|
|
69
|
+
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
|
|
70
|
+
sleep 5
|
|
71
|
+
curl -sf http://localhost:3000 > /dev/null || (echo "Static server failed to start" && exit 1)
|
|
72
|
+
|
|
73
|
+
- name: Check for ZAP rules file
|
|
74
|
+
id: check_rules
|
|
75
|
+
run: |
|
|
76
|
+
if [ -f "${{ inputs.zap_rules_file }}" ]; then
|
|
77
|
+
echo "has_rules=true" >> $GITHUB_OUTPUT
|
|
78
|
+
else
|
|
79
|
+
echo "has_rules=false" >> $GITHUB_OUTPUT
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
- name: Run ZAP baseline scan
|
|
83
|
+
uses: zaproxy/action-baseline@v0.14.0
|
|
84
|
+
with:
|
|
85
|
+
target: ${{ inputs.zap_target_url }}
|
|
86
|
+
rules_file_name: ${{ steps.check_rules.outputs.has_rules == 'true' && inputs.zap_rules_file || '' }}
|
|
87
|
+
fail_action: true
|
|
88
|
+
allow_issue_writing: false
|
|
89
|
+
artifact_name: 'zap-report-expo'
|
|
90
|
+
|
|
91
|
+
- name: Stop static server
|
|
92
|
+
if: always()
|
|
93
|
+
run: |
|
|
94
|
+
if [ -n "$SERVER_PID" ]; then
|
|
95
|
+
kill "$SERVER_PID" 2>/dev/null || true
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
- name: Upload ZAP report
|
|
99
|
+
if: always()
|
|
100
|
+
uses: actions/upload-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: zap-baseline-report-expo-${{ github.run_id }}
|
|
103
|
+
path: |
|
|
104
|
+
zap-report.html
|
|
105
|
+
zap-report.json
|
|
106
|
+
zap-report.md
|
|
107
|
+
retention-days: 14
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# OWASP ZAP Baseline Scan Configuration — Expo Web Apps
|
|
2
|
+
# Format: <rule_id> <action> <description>
|
|
3
|
+
# Actions: IGNORE (skip rule), WARN (report but don't fail), FAIL (fail on finding)
|
|
4
|
+
#
|
|
5
|
+
# Tuned for Expo static web exports served behind CDN/reverse proxy in production.
|
|
6
|
+
# Rules that are infrastructure-level (handled by CDN/hosting) are set to WARN.
|
|
7
|
+
|
|
8
|
+
# CSP header — Expo web exports typically need inline scripts for hydration;
|
|
9
|
+
# CSP is best enforced at the CDN/hosting layer with appropriate nonces.
|
|
10
|
+
10038 WARN (Content Security Policy (CSP) Header Not Set)
|
|
11
|
+
|
|
12
|
+
# X-Content-Type-Options — should be set at CDN/hosting layer
|
|
13
|
+
10021 WARN (X-Content-Type-Options Header Missing)
|
|
14
|
+
|
|
15
|
+
# Strict-Transport-Security — enforced at CDN/load balancer level
|
|
16
|
+
10035 WARN (Strict-Transport-Security Header Not Set)
|
|
17
|
+
|
|
18
|
+
# X-Frame-Options — enforced at CDN/hosting layer; CSP frame-ancestors preferred
|
|
19
|
+
10020 WARN (X-Frame-Options Header Not Set)
|
|
20
|
+
|
|
21
|
+
# Permissions-Policy — not critical for most Expo apps but good practice
|
|
22
|
+
10063 WARN (Permissions Policy Header Not Set)
|
|
23
|
+
|
|
24
|
+
# Server header disclosure — static server in CI, not production concern
|
|
25
|
+
10036 WARN (Server Leaks Version Information via "Server" HTTP Response Header Field)
|
|
26
|
+
|
|
27
|
+
# Cookie flags — Expo static sites typically don't set cookies
|
|
28
|
+
10010 IGNORE (Cookie No HttpOnly Flag)
|
|
29
|
+
10011 IGNORE (Cookie Without Secure Flag)
|
|
30
|
+
10054 IGNORE (Cookie without SameSite Attribute)
|
|
31
|
+
|
|
32
|
+
# Information disclosure in URL — Expo router may use query params legitimately
|
|
33
|
+
10024 WARN (Information Disclosure - Sensitive Information in URL)
|
|
34
|
+
|
|
35
|
+
# Cross-domain JavaScript source — Expo uses CDN-hosted scripts legitimately
|
|
36
|
+
10017 WARN (Cross-Domain JavaScript Source File Inclusion)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is managed by Lisa.
|
|
3
|
+
* Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Jest Configuration - Main Entry Point (Expo)
|
|
8
|
+
*
|
|
9
|
+
* Imports the Expo-specific configuration and project-local customizations.
|
|
10
|
+
* Do not modify this file directly - use jest.config.local.ts for project-specific settings.
|
|
11
|
+
*
|
|
12
|
+
* Inheritance chain:
|
|
13
|
+
* jest.config.ts (this file)
|
|
14
|
+
* └── jest.expo.ts
|
|
15
|
+
* └── jest.base.ts
|
|
16
|
+
*
|
|
17
|
+
* @see https://jestjs.io/docs/configuration
|
|
18
|
+
* @module jest.config
|
|
19
|
+
*/
|
|
20
|
+
import { mergeConfigs, mergeThresholds } from "./jest.base.ts";
|
|
21
|
+
import { defaultThresholds, getExpoJestConfig } from "./jest.expo.ts";
|
|
22
|
+
|
|
23
|
+
import localConfig from "./jest.config.local.ts";
|
|
24
|
+
import thresholdsOverrides from "./jest.thresholds.json" with { type: "json" };
|
|
25
|
+
|
|
26
|
+
const thresholds = mergeThresholds(defaultThresholds, thresholdsOverrides);
|
|
27
|
+
|
|
28
|
+
export default mergeConfigs(getExpoJestConfig({ thresholds }), localConfig);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is managed by Lisa.
|
|
3
|
+
* Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Jest Configuration - Expo Stack
|
|
8
|
+
*
|
|
9
|
+
* Provides Expo/React Native-specific Jest configuration without using
|
|
10
|
+
* the `jest-expo` preset directly. The preset's `setupFiles` include
|
|
11
|
+
* `react-native/jest/setup.js` which redefines `window` via
|
|
12
|
+
* `Object.defineProperties` — incompatible with jsdom's non-configurable
|
|
13
|
+
* `window` property, causing "Cannot redefine property: window" errors.
|
|
14
|
+
*
|
|
15
|
+
* Instead, this config manually replicates the preset's resolution,
|
|
16
|
+
* transform, and haste settings while using only `jest-expo/src/preset/setup.js`
|
|
17
|
+
* (the safe subset) in `setupFiles`.
|
|
18
|
+
*
|
|
19
|
+
* Coverage collection is scoped to standard Expo source directories
|
|
20
|
+
* rather than a catch-all glob, preventing config files, scripts, and
|
|
21
|
+
* plugins from distorting coverage numbers.
|
|
22
|
+
*
|
|
23
|
+
* Inheritance chain:
|
|
24
|
+
* jest.expo.ts (this file)
|
|
25
|
+
* └── jest.base.ts
|
|
26
|
+
*
|
|
27
|
+
* @see https://jestjs.io/docs/configuration
|
|
28
|
+
* @see https://github.com/expo/expo/issues/40184
|
|
29
|
+
* @module jest.expo
|
|
30
|
+
*/
|
|
31
|
+
import type { Config } from "jest";
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
defaultCoverageExclusions,
|
|
35
|
+
defaultThresholds,
|
|
36
|
+
mergeConfigs,
|
|
37
|
+
mergeThresholds,
|
|
38
|
+
} from "./jest.base.ts";
|
|
39
|
+
|
|
40
|
+
// Re-export base utilities for entry-point configs
|
|
41
|
+
export {
|
|
42
|
+
defaultCoverageExclusions,
|
|
43
|
+
defaultThresholds,
|
|
44
|
+
mergeConfigs,
|
|
45
|
+
mergeThresholds,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Options for configuring the Expo Jest config factory.
|
|
50
|
+
*/
|
|
51
|
+
interface ExpoJestOptions {
|
|
52
|
+
/** Coverage thresholds (merged defaults + project overrides) */
|
|
53
|
+
readonly thresholds?: Config["coverageThreshold"];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a Jest configuration for Expo/React Native projects.
|
|
58
|
+
*
|
|
59
|
+
* @param options - Configuration options for threshold overrides
|
|
60
|
+
* @param options.thresholds - Coverage thresholds (merged defaults + project overrides)
|
|
61
|
+
* @returns Jest config object with jsdom environment, babel-jest transform, and React Native resolver
|
|
62
|
+
* @remarks Avoids `jest-expo` preset to prevent jsdom + `react-native/jest/setup.js`
|
|
63
|
+
* incompatibility. Manually configures haste, resolver, transform, and setupFiles
|
|
64
|
+
* to match the preset's behavior without the problematic window redefinition.
|
|
65
|
+
*/
|
|
66
|
+
export const getExpoJestConfig = ({
|
|
67
|
+
thresholds = defaultThresholds,
|
|
68
|
+
}: ExpoJestOptions = {}): Config => ({
|
|
69
|
+
testEnvironment: "jsdom",
|
|
70
|
+
haste: {
|
|
71
|
+
defaultPlatform: "ios",
|
|
72
|
+
platforms: ["android", "ios", "native"],
|
|
73
|
+
},
|
|
74
|
+
resolver: "react-native/jest/resolver.js",
|
|
75
|
+
setupFiles: ["jest-expo/src/preset/setup.js"],
|
|
76
|
+
transform: {
|
|
77
|
+
"\\.[jt]sx?$": [
|
|
78
|
+
"babel-jest",
|
|
79
|
+
{
|
|
80
|
+
caller: {
|
|
81
|
+
name: "metro",
|
|
82
|
+
bundler: "metro",
|
|
83
|
+
platform: "ios",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
"^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp|ttf|otf|woff|woff2)$":
|
|
88
|
+
"jest-expo/src/preset/assetFileTransformer.js",
|
|
89
|
+
},
|
|
90
|
+
testMatch: [
|
|
91
|
+
"<rootDir>/**/*.test.ts",
|
|
92
|
+
"<rootDir>/**/*.test.tsx",
|
|
93
|
+
"<rootDir>/**/__tests__/**/*.ts",
|
|
94
|
+
"<rootDir>/**/__tests__/**/*.tsx",
|
|
95
|
+
],
|
|
96
|
+
testPathIgnorePatterns: ["/node_modules/", "/dist/", "/.expo/"],
|
|
97
|
+
transformIgnorePatterns: [
|
|
98
|
+
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|@gluestack-ui/.*|@gluestack-style/.*|nativewind|react-native-css-interop)",
|
|
99
|
+
],
|
|
100
|
+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
|
101
|
+
collectCoverageFrom: [
|
|
102
|
+
"app/**/*.{ts,tsx}",
|
|
103
|
+
"components/**/*.{ts,tsx}",
|
|
104
|
+
"config/**/*.{ts,tsx}",
|
|
105
|
+
"constants/**/*.{ts,tsx}",
|
|
106
|
+
"features/**/*.{ts,tsx}",
|
|
107
|
+
"hooks/**/*.{ts,tsx}",
|
|
108
|
+
"lib/**/*.{ts,tsx}",
|
|
109
|
+
"providers/**/*.{ts,tsx}",
|
|
110
|
+
"shared/**/*.{ts,tsx}",
|
|
111
|
+
"stores/**/*.{ts,tsx}",
|
|
112
|
+
"types/**/*.{ts,tsx}",
|
|
113
|
+
"utils/**/*.{ts,tsx}",
|
|
114
|
+
"!**/*.d.ts",
|
|
115
|
+
...defaultCoverageExclusions,
|
|
116
|
+
],
|
|
117
|
+
coverageThreshold: thresholds,
|
|
118
|
+
testTimeout: 10000,
|
|
119
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# OWASP ZAP Baseline Scan — Expo Web Export
|
|
3
|
+
# Builds the Expo web export, serves it locally, and runs a ZAP baseline scan via Docker.
|
|
4
|
+
# Outputs an HTML report to zap-report.html in the project root.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
9
|
+
TARGET_URL="${ZAP_TARGET_URL:-http://host.docker.internal:3000}"
|
|
10
|
+
ZAP_RULES_FILE="${ZAP_RULES_FILE:-.zap/baseline.conf}"
|
|
11
|
+
REPORT_FILE="zap-report.html"
|
|
12
|
+
|
|
13
|
+
cd "$PROJECT_ROOT"
|
|
14
|
+
|
|
15
|
+
# Verify Docker is available
|
|
16
|
+
if ! command -v docker &> /dev/null; then
|
|
17
|
+
echo "Error: Docker is required but not installed."
|
|
18
|
+
echo "Install Docker from https://docs.docker.com/get-docker/"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if ! docker info &> /dev/null 2>&1; then
|
|
23
|
+
echo "Error: Docker daemon is not running."
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Detect package manager
|
|
28
|
+
if [ -f "bun.lockb" ]; then
|
|
29
|
+
PKG_MGR="bun"
|
|
30
|
+
elif [ -f "yarn.lock" ]; then
|
|
31
|
+
PKG_MGR="yarn"
|
|
32
|
+
elif [ -f "pnpm-lock.yaml" ]; then
|
|
33
|
+
PKG_MGR="pnpm"
|
|
34
|
+
else
|
|
35
|
+
PKG_MGR="npm"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
echo "==> Building Expo web export..."
|
|
39
|
+
npx expo export --platform web
|
|
40
|
+
|
|
41
|
+
echo "==> Starting static server on port 3000..."
|
|
42
|
+
npx serve dist -l 3000 &
|
|
43
|
+
SERVER_PID=$!
|
|
44
|
+
|
|
45
|
+
cleanup() {
|
|
46
|
+
echo "==> Cleaning up..."
|
|
47
|
+
if [ -n "${SERVER_PID:-}" ]; then
|
|
48
|
+
kill "$SERVER_PID" 2>/dev/null || true
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
trap cleanup EXIT
|
|
52
|
+
|
|
53
|
+
sleep 3
|
|
54
|
+
if ! curl -sf http://localhost:3000 > /dev/null 2>&1; then
|
|
55
|
+
echo "Error: Static server failed to start"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
echo "==> Running OWASP ZAP baseline scan..."
|
|
60
|
+
ZAP_ARGS="-t $TARGET_URL"
|
|
61
|
+
|
|
62
|
+
if [ -f "$ZAP_RULES_FILE" ]; then
|
|
63
|
+
echo " Using rules file: $ZAP_RULES_FILE"
|
|
64
|
+
ZAP_ARGS="$ZAP_ARGS -c /zap/wrk/$(basename "$ZAP_RULES_FILE")"
|
|
65
|
+
MOUNT_RULES="-v $(dirname "$(realpath "$ZAP_RULES_FILE")"):/zap/wrk:ro"
|
|
66
|
+
else
|
|
67
|
+
MOUNT_RULES=""
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
docker run --rm \
|
|
71
|
+
--add-host=host.docker.internal:host-gateway \
|
|
72
|
+
-v "$(pwd)":/zap/wrk/:rw \
|
|
73
|
+
$MOUNT_RULES \
|
|
74
|
+
ghcr.io/zaproxy/zaproxy:stable \
|
|
75
|
+
zap-baseline.py $ZAP_ARGS \
|
|
76
|
+
-r "$REPORT_FILE" \
|
|
77
|
+
-J zap-report.json \
|
|
78
|
+
-w zap-report.md \
|
|
79
|
+
-l WARN || ZAP_EXIT=$?
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
if [ -f "$REPORT_FILE" ]; then
|
|
83
|
+
echo "ZAP report saved to: $REPORT_FILE"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [ "${ZAP_EXIT:-0}" -ne 0 ]; then
|
|
87
|
+
echo "ZAP found medium+ severity findings (exit code: $ZAP_EXIT)"
|
|
88
|
+
echo "Review $REPORT_FILE for details."
|
|
89
|
+
exit "$ZAP_EXIT"
|
|
90
|
+
else
|
|
91
|
+
echo "ZAP baseline scan passed — no medium+ severity findings."
|
|
92
|
+
fi
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["expo/tsconfig.base", "./tsconfig.base.json"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"jsx": "react-native",
|
|
6
|
+
"baseUrl": "./",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@/*": ["./*"]
|
|
9
|
+
},
|
|
10
|
+
"moduleSuffixes": [".ios", ".android", ".native", ".web", ""]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -25,6 +25,14 @@ jobs:
|
|
|
25
25
|
node_version: '22.21.1'
|
|
26
26
|
package_manager: 'bun'
|
|
27
27
|
|
|
28
|
+
zap:
|
|
29
|
+
name: 🕷️ ZAP Baseline Scan
|
|
30
|
+
needs: [quality]
|
|
31
|
+
uses: ./.github/workflows/zap-baseline.yml
|
|
32
|
+
with:
|
|
33
|
+
node_version: '22.21.1'
|
|
34
|
+
package_manager: 'bun'
|
|
35
|
+
|
|
28
36
|
create_issue_on_failure:
|
|
29
37
|
name: 📌 Create Issue on Failure
|
|
30
38
|
needs: [quality]
|