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
46 changes: 23 additions & 23 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
name: Build contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

# Cache Scarb / cargo artifacts and any (future) root-level
# node_modules. Keyed on the lockfile hash so a dependency change
Expand All @@ -32,7 +32,7 @@ jobs:
# included so that when npm-based jobs are added in the future,
# the cache key already covers them.
- name: Cache Scarb, Cargo and node_modules
uses: actions/cache@v4
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: |
~/.cargo/registry
Expand All @@ -46,9 +46,9 @@ jobs:
restore-keys: |
${{ runner.os }}-scarb-cargo-

- uses: software-mansion/setup-scarb@v1
- uses: software-mansion/setup-scarb@2a96b748888e3329ee44ac9ac073d930e692b3cd # v1.6.2
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
targets: wasm32-unknown-unknown
components: rustfmt
Expand All @@ -66,7 +66,7 @@ jobs:
# Audit dependencies for security advisories, license compliance,
# and duplicate crate versions.
- name: Install cargo-deny
uses: taiki-e/install-action@v2
uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2.87.2
with:
tool: cargo-deny

Expand All @@ -78,10 +78,10 @@ jobs:
name: Test contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Cache Scarb, Cargo and node_modules
uses: actions/cache@v4
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: |
~/.cargo/registry
Expand All @@ -95,14 +95,14 @@ jobs:
restore-keys: |
${{ runner.os }}-scarb-cargo-

- uses: software-mansion/setup-scarb@v1
- uses: software-mansion/setup-scarb@2a96b748888e3329ee44ac9ac073d930e692b3cd # v1.6.2
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable

- name: Install cargo-deny
uses: taiki-e/install-action@v2
uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2.87.2
with:
tool: cargo-deny

Expand All @@ -127,7 +127,7 @@ jobs:
run: cargo test --workspace --locked -- bench_ --nocapture 2>&1 | tee bench-output.txt

- name: Upload bench artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: bench-output
path: onchain/bench-output.txt
Expand All @@ -141,10 +141,10 @@ jobs:
name: Backend lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand Down Expand Up @@ -176,10 +176,10 @@ jobs:
name: Backend tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand All @@ -204,10 +204,10 @@ jobs:
name: Frontend lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand Down Expand Up @@ -240,10 +240,10 @@ jobs:
name: Frontend build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand All @@ -265,10 +265,10 @@ jobs:
runs-on: ubuntu-latest
needs: frontend-build
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand Down Expand Up @@ -307,10 +307,10 @@ jobs:
name: Frontend tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
# Fetch all history so git-cliff / auto-changelog can read commits
fetch-depth: 0
Expand All @@ -36,12 +36,12 @@ jobs:

# ── Install toolchains ────────────────────────────────
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
targets: wasm32-unknown-unknown

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: npm
Expand Down Expand Up @@ -139,7 +139,7 @@ jobs:

# ── Create GitHub Release ─────────────────────────────
- name: Create Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
tag_name: ${{ github.ref_name }}
name: v${{ steps.tag.outputs.version }}
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ jobs:
language: [javascript-typescript]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@a35ac6e6798d72df5475948b28efb89edc2e19ca # v4.37.9
with:
languages: ${{ matrix.language }}

- name: Autobuild
uses: github/codeql-action/autobuild@v4
uses: github/codeql-action/autobuild@a35ac6e6798d72df5475948b28efb89edc2e19ca # v4.37.9

- name: Analyze
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@a35ac6e6798d72df5475948b28efb89edc2e19ca # v4.37.9
with:
category: /language:${{ matrix.language }}

Expand All @@ -50,7 +50,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

# Requires the repository's "Dependency graph" setting to be enabled
# (Settings → Code security and analysis). Until that is flipped, the
Expand All @@ -59,7 +59,7 @@ jobs:
# blocking gate as soon as the setting is enabled.
- name: Review dependency changes
continue-on-error: true
uses: actions/dependency-review-action@v5
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
fail-on-severity: high
fail-on-scopes: runtime, development
Expand Down Expand Up @@ -102,7 +102,7 @@ jobs:
security-events: write
steps:
- name: Checkout full history
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 0

Expand All @@ -119,13 +119,13 @@ jobs:

- name: Upload SARIF to code scanning
if: always()
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@a35ac6e6798d72df5475948b28efb89edc2e19ca # v4.37.9
with:
sarif_file: gitleaks.sarif

- name: Upload SARIF artifact
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: gitleaks-sarif
path: gitleaks.sarif
Expand Down
9 changes: 7 additions & 2 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { CsrfMiddleware } from './common/security/csrf.middleware';
import { NestModule, MiddlewareConsumer } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { join } from 'path';
Expand Down Expand Up @@ -147,4 +148,8 @@ import { GracefulShutdownService } from './graceful-shutdown.service';
controllers: [AppController],
providers: [AppService, GracefulShutdownService],
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(CsrfMiddleware).forRoutes('*');
}
}
17 changes: 17 additions & 0 deletions backend/src/common/security/csrf.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Injectable, NestMiddleware, ForbiddenException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class CsrfMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const method = req.method;
if (['GET', 'HEAD', 'OPTIONS'].includes(method)) {
return next();
}
const csrfToken = req.headers['x-csrf-token'];
if (!csrfToken) {
throw new ForbiddenException('CSRF token missing');
}
next();
}
}
25 changes: 23 additions & 2 deletions backend/src/puzzle-draft/draft-puzzle.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DraftPuzzle } from './entities/draft-puzzle.entity';
Expand Down Expand Up @@ -29,6 +29,23 @@ export class DraftPuzzleService {

async update(id: string, updateDto: UpdateDraftDto) {
const draft = await this.findOne(id);
if (draft.status === 'published') {
throw new BadRequestException('Cannot edit a published draft.');
}

if (updateDto.status) {
const allowedTransitions: Record<string, string[]> = {
draft: ['review'],
review: ['draft', 'approved'],
approved: ['review', 'published'],
published: []
};
const allowed = allowedTransitions[draft.status] || [];
if (!allowed.includes(updateDto.status)) {
throw new BadRequestException(`Invalid status transition from ${draft.status} to ${updateDto.status}`);
}
}

Object.assign(draft, updateDto);
return this.draftRepo.save(draft);
}
Expand All @@ -40,7 +57,11 @@ export class DraftPuzzleService {

async publish(id: string) {
const draft = await this.findOne(id);
// Emit event or return structured data for publishing module to handle
if (draft.status !== 'approved') {
throw new BadRequestException('Only approved drafts can be published.');
}
draft.status = 'published';
await this.draftRepo.save(draft);
return {
event: 'PUZZLE_DRAFT_PUBLISHED',
data: draft,
Expand Down
3 changes: 3 additions & 0 deletions backend/src/puzzle-draft/entities/draft-puzzle.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export class DraftPuzzle {
@Column()
createdBy: string;

@Column({ default: 'draft' })
status: string;

@CreateDateColumn()
createdAt: Date;

Expand Down
Loading