Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: setup gh format, lint and build checks workflow for back-end part #360

Merged
merged 2 commits into from
Aug 26, 2024
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
47 changes: 47 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: "Nest Back-End format, lint and build checks"

on:
pull_request:
branches:
- '**'

push:
branches:
- stable
- unstable

paths:
- 'src/**'
- 'test/**'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: npm ci

- name: Check format
run: npm run format

# commented till fixing lint errors
# - name: Lint
# run: npm run lint

- name: Build
run: npm run build
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"{src,public,test}/**/*.ts\"",
"format": "prettier --check \"{src,public,test}/**/*.ts\"",
"format:write": "prettier --write \"{src,public,test}/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
Expand Down
18 changes: 14 additions & 4 deletions src/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import { SWAGGER_DESC_FIND_USER } from './users/users.controller.swagger.desc';
export class AppController {
private readonly logger = new Logger(AppController.name);

constructor(private readonly appService: AppService) { }
constructor(private readonly appService: AppService) {}

@Post('render')
@ApiProduces('text/plain')
Expand Down Expand Up @@ -273,15 +273,25 @@ export class AppController {
description: SWAGGER_DESC_NESTED_JSON,
})
@Header('content-type', 'application/json')
async getNestedJson(@Query('depth', new DefaultValuePipe(1), new ParseIntPipe({ errorHttpStatusCode: HttpStatus.BAD_REQUEST })) depth: number): Promise<string> {
async getNestedJson(
@Query(
'depth',
new DefaultValuePipe(1),
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.BAD_REQUEST }),
)
depth: number,
): Promise<string> {
if (depth < 1) {
throw new HttpException("JSON nesting depth is invalid", HttpStatus.BAD_REQUEST);
throw new HttpException(
'JSON nesting depth is invalid',
HttpStatus.BAD_REQUEST,
);
}

this.logger.debug(`Creating a JSON with a nesting depth of ${depth}`);

var tmpObj: object = {};
var jsonObj: object = { "0": "Leaf" };
var jsonObj: object = { '0': 'Leaf' };
for (let i = 1; i < depth; i++) {
tmpObj = {};
tmpObj[i.toString()] = Object.assign({}, jsonObj);
Expand Down
2 changes: 1 addition & 1 deletion src/email/email.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class EmailController {
})
async getEmails(@Query('withSource') withSource: any) {
withSource = withSource === 'true';

this.logger.log(`Getting Emails (withSource=${withSource})`);
return await this.emailService.getEmails(withSource);
}
Expand Down
4 changes: 1 addition & 3 deletions src/httpclient/httpclient.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ export class HttpClientService {
return text;
}

async loadAny(
url: string,
): Promise<{
async loadAny(url: string): Promise<{
content: Buffer;
contentType: string;
}> {
Expand Down
233 changes: 124 additions & 109 deletions src/partners/partners.controller.ts
Original file line number Diff line number Diff line change
@@ -1,131 +1,146 @@
import {
Controller,
Get,
Header,
HttpException,
HttpStatus,
Logger,
Query,
Controller,
Get,
Header,
HttpException,
HttpStatus,
Logger,
Query,
} from '@nestjs/common';
import {
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import {
API_DESC_QUERY_PARTNERS_RAW,
API_DESC_PARTNERS_LOGIN,
API_DESC_SEARCH_PARTNERS_NAMES
API_DESC_QUERY_PARTNERS_RAW,
API_DESC_PARTNERS_LOGIN,
API_DESC_SEARCH_PARTNERS_NAMES,
} from './partners.controller.swagger.desc';
import {
PartnersService,
} from './partners.service';

import { PartnersService } from './partners.service';

@Controller('/api/partners')
@ApiTags('Partners controller')
export class PartnersController {
private readonly logger = new Logger(PartnersController.name);
private readonly logger = new Logger(PartnersController.name);

constructor(private readonly partnersService: PartnersService) { }
constructor(private readonly partnersService: PartnersService) {}

// **** This is a general XPATH injection EP - Will accept anything ****
@Get('query')
@ApiQuery({
name: 'xpath',
type: 'string',
example: '/partners/partner/name',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_QUERY_PARTNERS_RAW,
})
@ApiOkResponse({
type: String,
})
async queryPartnersRaw(@Query('xpath') xpath: string): Promise<String> {
this.logger.debug(`Getting partners with xpath expression "${xpath}"`);
// **** This is a general XPATH injection EP - Will accept anything ****
@Get('query')
@ApiQuery({
name: 'xpath',
type: 'string',
example: '/partners/partner/name',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_QUERY_PARTNERS_RAW,
})
@ApiOkResponse({
type: String,
})
async queryPartnersRaw(@Query('xpath') xpath: string): Promise<String> {
this.logger.debug(`Getting partners with xpath expression "${xpath}"`);

try {
return this.partnersService.getPartnersProperties(xpath);
} catch (err) {
throw new HttpException(`Failed to load XML using XPATH. Details: ${err}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
try {
return this.partnersService.getPartnersProperties(xpath);
} catch (err) {
throw new HttpException(
`Failed to load XML using XPATH. Details: ${err}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

// **** This is a boolean based XPATH injection EP ****
@Get('partnerLogin')
@ApiQuery({
name: 'username',
type: 'string',
example: 'walter100',
required: true,
})
@ApiQuery({
name: 'password',
type: 'string',
example: 'Heisenberg123',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_PARTNERS_LOGIN,
})
@ApiOkResponse({
type: String,
})
async partnerLogin(@Query('username') username: string, @Query('password') password: string): Promise<String> {
this.logger.debug(`Trying to login partner with username ${username} using password ${password}`);
// **** This is a boolean based XPATH injection EP ****
@Get('partnerLogin')
@ApiQuery({
name: 'username',
type: 'string',
example: 'walter100',
required: true,
})
@ApiQuery({
name: 'password',
type: 'string',
example: 'Heisenberg123',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_PARTNERS_LOGIN,
})
@ApiOkResponse({
type: String,
})
async partnerLogin(
@Query('username') username: string,
@Query('password') password: string,
): Promise<String> {
this.logger.debug(
`Trying to login partner with username ${username} using password ${password}`,
);

try {
let xpath = `//partners/partner[username/text()='${username}' and password/text()='${password}']/*`
let xmlStr = this.partnersService.getPartnersProperties(xpath);
try {
let xpath = `//partners/partner[username/text()='${username}' and password/text()='${password}']/*`;
let xmlStr = this.partnersService.getPartnersProperties(xpath);

// Check if account's data contains any information - If not, the login failed!
if (!(xmlStr && xmlStr.includes('password') && xmlStr.includes('wealth'))) {
throw new Error("Login attempt failed!");
}
// Check if account's data contains any information - If not, the login failed!
if (
!(xmlStr && xmlStr.includes('password') && xmlStr.includes('wealth'))
) {
throw new Error('Login attempt failed!');
}

return xmlStr;
} catch (err) {
let strErr = err.toString();
if (strErr.includes('Unterminated string literal')) {
err = 'Error in XPath expression'
}
throw new HttpException(`Access denied to partner's account. ${err}`, HttpStatus.FORBIDDEN);
}
return xmlStr;
} catch (err) {
let strErr = err.toString();
if (strErr.includes('Unterminated string literal')) {
err = 'Error in XPath expression';
}
throw new HttpException(
`Access denied to partner's account. ${err}`,
HttpStatus.FORBIDDEN,
);
}
}

// **** This is a string based XPATH injection EP ****
@Get('searchPartners')
@ApiQuery({
name: 'keyword',
type: 'string',
example: 'Walter',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_SEARCH_PARTNERS_NAMES,
})
@ApiOkResponse({
type: String,
})
async searchPartners(@Query('keyword') keyword: string): Promise<String> {
this.logger.debug(`Searching partner names by the keyword "${keyword}"`);

// **** This is a string based XPATH injection EP ****
@Get('searchPartners')
@ApiQuery({
name: 'keyword',
type: 'string',
example: 'Walter',
required: true,
})
@Header('content-type', 'text/xml')
@ApiOperation({
description: API_DESC_SEARCH_PARTNERS_NAMES,
})
@ApiOkResponse({
type: String,
})
async searchPartners(@Query('keyword') keyword: string): Promise<String> {
this.logger.debug(`Searching partner names by the keyword "${keyword}"`);

try {
let xpath = `//partners/partner/name[contains(., '${keyword}')]`
return this.partnersService.getPartnersProperties(xpath);
} catch (err) {
let strErr = err.toString();
if (strErr.includes('XPath parse error') || strErr.includes('Unterminated string literal')) {
err = 'Error in XPath expression'
}
throw new HttpException(`Couldn't find partners. ${err}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
try {
let xpath = `//partners/partner/name[contains(., '${keyword}')]`;
return this.partnersService.getPartnersProperties(xpath);
} catch (err) {
let strErr = err.toString();
if (
strErr.includes('XPath parse error') ||
strErr.includes('Unterminated string literal')
) {
err = 'Error in XPath expression';
}
throw new HttpException(
`Couldn't find partners. ${err}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
}
2 changes: 1 addition & 1 deletion src/partners/partners.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import { PartnersController } from './partners.controller';
controllers: [PartnersController],
exports: [PartnersService],
})
export class PartnersModule { }
export class PartnersModule {}
Loading