Skip to content
Merged
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
8,782 changes: 8,770 additions & 12 deletions backend/package-lock.json

Large diffs are not rendered by default.

45 changes: 41 additions & 4 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,58 @@
"build": "nest build",
"start": "nest start",
"start:dev": "nest start --watch",
"start:prod": "node dist/main"
"start:prod": "node dist/main",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r find-node-modules node_modules/.bin/jest --runInBand",
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'NODE'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('backend/package.json', 'utf8'));
console.log('declared:', pkg.dependencies?.['find-node-modules'] ?? pkg.devDependencies?.['find-node-modules'] ?? 'missing');
NODE

rg -n '"find-node-modules"' backend/package-lock.json backend/pnpm-lock.yaml backend/yarn.lock 2>/dev/null || true
node -e "require.resolve('find-node-modules'); console.log('resolves')"

Repository: Alien-Protocol/Alien-Protocol

Length of output: 906


Add find-node-modules to devDependencies or remove it from the test:debug preload.

The test:debug script preloads -r find-node-modules, but this package is not declared in the manifest and cannot be resolved. On a clean install, npm run test:debug will fail with MODULE_NOT_FOUND.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/package.json` at line 13, The npm script "test:debug" in package.json
preloads "-r find-node-modules" which is not declared, causing MODULE_NOT_FOUND;
either add "find-node-modules" to devDependencies (so npm install will provide
it) or remove the "-r find-node-modules" preload from the "test:debug" script;
update package.json accordingly and run npm install to verify the script runs
(refer to the "test:debug" script entry in package.json to locate the change).

"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.4",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
"swagger-ui-express": "^5.0.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@types/express": "^4.17.0",
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^2.0.12",
"jest": "^29.5.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}


5 changes: 5 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ResolverModule } from './resolver/resolver.module';
import { VaultModule } from './vault/vault.module';
import { AuctionModule } from './auction/auction.module';

@Module({
imports: [ResolverModule, VaultModule, AuctionModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

8 changes: 8 additions & 0 deletions backend/src/auction/dto/auction.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IsNotEmpty, IsNumberString, Matches } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class AuctionInfoDto {
Expand Down Expand Up @@ -45,12 +46,19 @@ export class AuctionListItemDto {

export class PlaceBidDto {
@ApiProperty({ description: 'Bidder Stellar address', example: 'GXYZ9876STELLAR1234BIDDERADDRESS' })
@IsNotEmpty()
@Matches(/^G[A-Z2-7]{55}$/, {
message: 'Invalid Stellar wallet address format',
})
bidder: string;
Comment on lines 48 to 53
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 | 🟡 Minor

Keep the bid example consistent with the Stellar regex.

bidder's example also fails ^G[A-Z2-7]{55}$, so Swagger is advertising an invalid sample payload here as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/auction/dto/auction.dto.ts` around lines 31 - 36, The Swagger
ApiProperty example for the bidder field does not match the Stellar address
regex used by the `@Matches` validator; update the example in the ApiProperty for
the bidder property so it is a valid 56-character Stellar public key (starts
with 'G' and uses only A-Z and 2-7) to satisfy the regex in the Matches
decorator (referenced symbol: bidder, decorators: `@ApiProperty` and
`@Matches`(/^G[A-Z2-7]{55}$/)).


@ApiProperty({ description: 'Bid amount in XLM', example: '150.00' })
@IsNotEmpty()
@IsNumberString()
amount: string;
}


export class BidResponseDto {
@ApiProperty({ description: 'Whether the bid was accepted', example: true })
success: boolean;
Expand Down
17 changes: 17 additions & 0 deletions backend/src/auth/dto/login.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString, Matches } from 'class-validator';

export class LoginDto {
@ApiProperty({ description: 'Stellar wallet address', example: 'GABC1234STELLAR5678WALLETADDRESS' })
@IsString()
@IsNotEmpty()
@Matches(/^G[A-Z2-7]{55}$/, {
message: 'Invalid Stellar wallet address format',
})
address: string;
Comment on lines +5 to +11
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 | 🟡 Minor

Use a Swagger example that satisfies the validator.

GABC1234STELLAR5678WALLETADDRESS does not match ^G[A-Z2-7]{55}$, so the documented sample request is self-contradictory and will fail if copied into a client.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/auth/dto/login.dto.ts` around lines 5 - 11, The Swagger example
for the address field conflicts with its validator: update the ApiProperty
example on the address property so it satisfies the Matches(/^G[A-Z2-7]{55}$/)
constraint — provide a 56-character string that starts with 'G' and contains
only A–Z and digits 2–7 (i.e., change the example in the ApiProperty decorator
for the address field to a value matching the regex).


@ApiProperty({ description: 'Signature for authentication', example: 'sig_abc123...' })
@IsString()
@IsNotEmpty()
signature: string;
}
9 changes: 9 additions & 0 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);

const config = new DocumentBuilder()
.setTitle('Alien Gateway API')
.setVersion('1.0')
Expand Down
17 changes: 17 additions & 0 deletions backend/src/resolver/dto/resolver.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IsNotEmpty, IsString, Matches } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class ResolveResponseDto {
Expand All @@ -13,15 +14,31 @@ export class ResolveResponseDto {

export class RegisterUsernameDto {
@ApiProperty({ description: 'Username to register (without @)', example: 'alice' })
@IsString()
@IsNotEmpty()
@Matches(/^[a-zA-Z0-9_]{3,20}$/, {
message: 'Username must be 3-20 characters long and contain only letters, numbers, and underscores',
})
username: string;

@ApiProperty({ description: 'Stellar wallet address to link', example: 'GABC1234STELLAR5678WALLETADDRESS' })
@IsString()
@IsNotEmpty()
@Matches(/^G[A-Z2-7]{55}$/, {
message: 'Invalid Stellar wallet address format',
})
walletAddress: string;

@ApiProperty({ description: 'Zero-knowledge commitment hash', example: '0xabc123...' })
@IsString()
@IsNotEmpty()
@Matches(/^0x[a-fA-F0-9]{64}$/, {
message: 'Commitment must be a 32-byte hex string prefixed with 0x',
})
Comment on lines 16 to +37
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 | 🟡 Minor

Align the Swagger examples with the new validation rules.

The current walletAddress and commitment examples do not satisfy the regex constraints added below, so the generated docs will mislead API consumers and produce copy-paste payloads that fail validation.

💡 Suggested update
   `@ApiProperty`({ description: 'Stellar wallet address to link', example: 'GABC1234STELLAR5678WALLETADDRESS' })
+  `@ApiProperty`({
+    description: 'Stellar wallet address to link',
+    example: `G${'A'.repeat(55)}`,
+  })
@@
-  `@ApiProperty`({ description: 'Zero-knowledge commitment hash', example: '0xabc123...' })
+  `@ApiProperty`({
+    description: 'Zero-knowledge commitment hash',
+    example: `0x${'a'.repeat(64)}`,
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/resolver/dto/resolver.dto.ts` around lines 16 - 37, Update the
ApiProperty examples to match the new validation regexes: replace the
walletAddress example with a valid 56-char Stellar public key starting with 'G'
and containing only A-Z and 2-7 (e.g. a 56-character string like "G" + 55 valid
base32 chars) in the walletAddress property, and replace the commitment example
with a 32-byte hex string prefixed by 0x (64 hex chars after 0x) for the
commitment property so they pass the `@Matches`(/^G[A-Z2-7]{55}$/) and
`@Matches`(/^0x[a-fA-F0-9]{64}$/) validators respectively; ensure the username
example remains valid for `@Matches`(/^[a-zA-Z0-9_]{3,20}$/).

commitment: string;
}


export class RegisterResponseDto {
@ApiProperty({ description: 'Whether registration succeeded', example: true })
success: boolean;
Expand Down
10 changes: 10 additions & 0 deletions backend/src/vault/dto/vault.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IsBoolean, IsIn, IsNotEmpty, IsNumberString, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class VaultBalanceDto {
Expand Down Expand Up @@ -30,18 +31,27 @@ export class PaymentDto {

export class AutoPayDto {
@ApiProperty({ description: 'Auto-pay rule ID', example: 'ap_xyz789' })
@IsString()
@IsNotEmpty()
id: string;

@ApiProperty({ description: 'Recipient username', example: 'bob' })
@IsString()
@IsNotEmpty()
recipient: string;

@ApiProperty({ description: 'Amount per interval in XLM', example: '5.00' })
@IsNotEmpty()
@IsNumberString()
amount: string;

@ApiProperty({ description: 'Payment interval', example: 'monthly', enum: ['daily', 'weekly', 'monthly'] })
@IsNotEmpty()
@IsIn(['daily', 'weekly', 'monthly'])
interval: string;

@ApiProperty({ description: 'Whether the rule is active', example: true })
@IsBoolean()
active: boolean;
}

Expand Down
5 changes: 2 additions & 3 deletions backend/test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { App } from 'supertest/types';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (e2e)', () => {
let app: INestApplication<App>;
let app: INestApplication;

beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
Expand Down
102 changes: 102 additions & 0 deletions backend/test/validation.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('Global Validation (e2e)', () => {
let app: INestApplication;

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.init();
});

afterAll(async () => {
await app.close();
});

describe('Resolver Registration Validation', () => {
it('should return 400 when username is too short', () => {
return request(app.getHttpServer())
.post('/resolver/register')
.send({
username: 'al',
walletAddress: 'GABC1234STELLAR5678WALLETADDRESS',
commitment: '0xabc1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd',
})
.expect(400)
.expect((res) => {
expect(res.body.message).toContain('Username must be 3-20 characters long and contain only letters, numbers, and underscores');
});
});

it('should return 400 when wallet address is invalid', () => {
return request(app.getHttpServer())
.post('/resolver/register')
.send({
username: 'alice',
walletAddress: 'INVALID_ADDRESS',
commitment: '0xabc1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd',
})
.expect(400)
.expect((res) => {
expect(res.body.message).toContain('Invalid Stellar wallet address format');
});
});

it('should return 400 when commitment is not a hex string', () => {
return request(app.getHttpServer())
.post('/resolver/register')
.send({
username: 'alice',
walletAddress: 'GABC1234STELLAR5678WALLETADDRESS',
commitment: 'not-a-hex-string',
})
.expect(400)
.expect((res) => {
expect(res.body.message).toContain('Commitment must be a 32-byte hex string prefixed with 0x');
});
});

it('should return 400 when extra fields are provided (forbidNonWhitelisted)', () => {
return request(app.getHttpServer())
.post('/resolver/register')
.send({
username: 'alice',
walletAddress: 'GABC1234STELLAR5678WALLETADDRESS',
commitment: '0xabc1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd',
extraField: 'should-not-be-here',
})
.expect(400)
.expect((res) => {
expect(res.body.message).toContain('property extraField should not exist');
});
});
});

describe('Auction Bid Validation', () => {
it('should return 400 when amount is not a number string', () => {
return request(app.getHttpServer())
.post('/auction/1/bid')
.send({
bidder: 'GABC1234STELLAR5678WALLETADDRESS',
amount: 'not-a-number',
})
.expect(400)
.expect((res) => {
expect(res.body.message).toContain('amount must be a number string');
});
});
});
});
Loading