CVE-2019-16281: Improper Certificate Validation (CWE-295)
value: Ptarmigan before 0.2.3 lacks API token validation, e.g., an "if (token === apiToken) {return true;} return false;" code block.
CWE description: The product does not validate, or incorrectly validates, a certificate.
CVSS v3 base score: 7.5
Published: 2020-12-30T21:15Z
Repository: https://github.com/nayutaco/ptarmigan
Language: TypeScript
Files changed: ptarmapi/.env-sample, ptarmapi/package.json, ptarmapi/script/addfinal.sh, ptarmapi/script/htlcchanged.sh, ptarmapi/src/app.module.ts, ptarmapi/src/auth/auth.module.ts, ptarmapi/src/auth/auth.service.ts, ptarmapi/src/auth/http.strategy.ts, ptarmapi/src/config/ptarmigan.ts, ptarmapi/src/main.ts, ptarmapi/src/notifications/invoices.gateway.ts, ptarmapi/src/ptarmigan/ptarmigan.controller.ts, ptarmapi/test/client/index.html
Commit date: 2019-07-10 17:35:35 +0900
Commit message: add api token in rest api

add apiBeareAuth

add token in .env-sample

Diff: diff --git a/ptarmapi/.env-sample b/ptarmapi/.env-sample
index e2fe0dce..da8a1356 100644
--- a/ptarmapi/.env-sample
+++ b/ptarmapi/.env-sample
@@ -4,5 +4,6 @@ PTARMD_RPC_PORT=9736
 PTARMD_HOST="localhost"
 BITCOIND_RPC_PORT=18332
 BITCOIND_HOST="localhost"
 BITCOIND_USER="bitcoinuser"
 BITCOIND_PASS="bitcoinpassword"
+API_TOKEN="ptarmigan"
diff --git a/ptarmapi/package.json b/ptarmapi/package.json
index b3429cd6..06e5303b 100644
--- a/ptarmapi/package.json
+++ b/ptarmapi/package.json
@@ -24,10 +24,11 @@
   },
   "dependencies": {
     "@nestjs/common": "^6.4.1",
     "@nestjs/core": "^6.4.1",
     "@nestjs/microservices": "^6.4.1",
+    "@nestjs/passport": "^6.1.0",
     "@nestjs/platform-express": "^6.4.1",
     "@nestjs/platform-socket.io": "^6.4.1",
     "@nestjs/platform-ws": "^6.4.1",
     "@nestjs/swagger": "^3.0.2",
     "@nestjs/websockets": "^6.4.1",
@@ -35,10 +36,12 @@
     "class-validator": "^0.9.1",
     "dotenv": "^7.0.0",
     "jayson": "^2.1.2",
     "lru-cache": "^5.1.1",
     "nestjs-config": "^1.3.21",
+    "passport": "^0.4.0",
+    "passport-http-bearer": "^1.0.1",
     "reflect-metadata": "^0.1.13",
     "request-promise": "^4.2.4",
     "rimraf": "^2.6.2",
     "rxjs": "6.5.2",
     "swagger-ui-express": "^4.0.6",
@@ -74,6 +77,6 @@
       "^.+\\.(t|j)s$": "ts-jest"
     },
     "coverageDirectory": "../coverage",
     "testEnvironment": "node"
   }
-}
\ No newline at end of file
+}
diff --git a/ptarmapi/script/addfinal.sh b/ptarmapi/script/addfinal.sh
index bb6b6145..41084f66 100755
--- a/ptarmapi/script/addfinal.sh
+++ b/ptarmapi/script/addfinal.sh
@@ -23,15 +23,19 @@ EOS
 json=$(cat << EOS
 {"paymentHash":"$3"}
 EOS
 )
 
+if [ "${API_TOKEN:-unknown}" == unknown ]; then
+  API_TOKEN=ptarmigan
+fi
+
 curl=`cat <<EOS
 curl
  -s -o
- --verbose
  --request POST
  http://0.0.0.0:3000/notification/addfinal
  --header 'Content-Type: application/json'
+ --header "Authorization: Bearer $API_TOKEN"
  --data '$json'
 EOS`
 eval ${curl}
\ No newline at end of file
diff --git a/ptarmapi/script/htlcchanged.sh b/ptarmapi/script/htlcchanged.sh
index 6a81142e..4190f3e4 100755
--- a/ptarmapi/script/htlcchanged.sh
+++ b/ptarmapi/script/htlcchanged.sh
@@ -14,14 +14,18 @@ cat << EOS | jq -e '.'
     "node_id":"$2",
     "local_msat":$3
 }
 EOS
 
+if [ "${API_TOKEN:-unknown}" == unknown ]; then
+  API_TOKEN=ptarmigan
+fi
+
 curl=`cat <<EOS
 curl
  -s -o
- --verbose
  --request POST
  http://0.0.0.0:3000/notification/htlcchanged
  --header 'Content-Type: application/json'
+ --header "Authorization: Bearer $API_TOKEN"
 EOS`
 eval ${curl}
\ No newline at end of file
diff --git a/ptarmapi/src/app.module.ts b/ptarmapi/src/app.module.ts
index 87cf1721..9b2bfd19 100644
--- a/ptarmapi/src/app.module.ts
+++ b/ptarmapi/src/app.module.ts
@@ -5,16 +5,17 @@ import { PtarmiganService } from './ptarmigan/ptarmigan.service';
 import { PtarmiganController } from './ptarmigan/ptarmigan.controller';
 import { ConfigModule } from 'nestjs-config';
 import { BitcoinService } from './bitcoin/bitcoin.service';
 import { CacheService } from './cache/cache.servies';
 import { InvoicesGateway } from './notifications/invoices.gateway';
-
+import { AuthModule } from './auth/auth.module';
 import * as path from 'path';
 
 @Module({
   controllers: [AppController, PtarmiganController],
   providers: [AppService, PtarmiganService, BitcoinService, CacheService, InvoicesGateway],
   imports: [
+    AuthModule,
     ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')),
   ],
 })
 export class AppModule { }
diff --git a/ptarmapi/src/auth/auth.module.ts b/ptarmapi/src/auth/auth.module.ts
new file mode 100644
index 00000000..54a951a4
--- /dev/null
+++ b/ptarmapi/src/auth/auth.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { HttpStrategy } from './http.strategy';
+import { PassportModule } from '@nestjs/passport';
+
+@Module({
+ imports: [
+  PassportModule.register({ defaultStrategy: 'bearer' }),
+ ],
+ providers: [AuthService, HttpStrategy],
+ exports: [PassportModule, AuthService],
+})
+export class AuthModule {}
diff --git a/ptarmapi/src/auth/auth.service.ts b/ptarmapi/src/auth/auth.service.ts
new file mode 100644
index 00000000..5d8075fb
--- /dev/null
+++ b/ptarmapi/src/auth/auth.service.ts
@@ -0,0 +1,16 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigService } from 'nestjs-config';
+
+@Injectable()
+export class AuthService {
+
+  constructor() { }
+
+  async validateApiToken(token: string): Promise<any> {
+    const apiToken = ConfigService.get('ptarmigan.apiToken');
+    if (token === apiToken) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/ptarmapi/src/auth/http.strategy.ts b/ptarmapi/src/auth/http.strategy.ts
new file mode 100644
index 00000000..4f1234aa
--- /dev/null
+++ b/ptarmapi/src/auth/http.strategy.ts
@@ -0,0 +1,19 @@
+import { Strategy } from 'passport-http-bearer';
+import { PassportStrategy } from '@nestjs/passport';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class HttpStrategy extends PassportStrategy(Strategy) {
+  constructor(private readonly authService: AuthService) {
+    super();
+  }
+
+  async validate(token: string) {
+    const apiToken = await this.authService.validateApiToken(token);
+    if (!apiToken) {
+      throw new UnauthorizedException();
+    }
+    return apiToken;
+  }
+}
diff --git a/ptarmapi/src/config/ptarmigan.ts b/ptarmapi/src/config/ptarmigan.ts
index 978b7c29..df6104b0 100644
--- a/ptarmapi/src/config/ptarmigan.ts
+++ b/ptarmapi/src/config/ptarmigan.ts
@@ -5,6 +5,7 @@ export default {
     ptarmdHost: process.env.PTARMD_HOST || 'localhost',
     bitcoindRpcPort: process.env.BITCOIND_RPC_PORT || 18332,
     bitcoindHost: process.env.BITCOIND_HOST || 'localhost',
     bitcoindUser: process.env.BITCOIND_USER || 'bitcoinuser',
     bitcoindPassword: process.env.BITCOIND_PASSWORD || 'bitcoinpassword',
+    apiToken: process.env.API_TOKEN || 'ptarmigan',
 };
diff --git a/ptarmapi/src/main.ts b/ptarmapi/src/main.ts
index 37e26fde..55fd74b8 100644
--- a/ptarmapi/src/main.ts
+++ b/ptarmapi/src/main.ts
@@ -1,7 +1,7 @@
 import { NestFactory } from '@nestjs/core';
-import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
+import { SwaggerModule, DocumentBuilder, SwaggerBaseConfig } from '@nestjs/swagger';
 import { AppModule } from './app.module';
 import { Logger } from '@nestjs/common';
 import { ConfigService } from 'nestjs-config';
 import { NestExpressApplication } from '@nestjs/platform-express';
 import { WsAdapter } from '@nestjs/platform-ws';
@@ -12,19 +12,21 @@ async function bootstrap() {
 
   // swagger setting
   const options = new DocumentBuilder()
     .setTitle('ptarmigan REST-API')
     .setDescription('Lightning Network implementation ptarmigan REST-API')
+    .addBearerAuth('Authorization', 'header')
     .setVersion('0.2')
     .build();
   const document = SwaggerModule.createDocument(app, options);
   SwaggerModule.setup('api', app, document);
 
   const config = ConfigService;
   Logger.log('ptarmdRpcPort: ' + config.get('ptarmigan.ptarmdRpcPort'));
   Logger.log('ptarmdHost: + ' + config.get('ptarmigan.ptarmdHost'));
   Logger.log('bitcoindRpcPort: ' + config.get('ptarmigan.bitcoindRpcPort'));
   Logger.log('bitcoindHost: ' + config.get('ptarmigan.bitcoindHost'));
+  Logger.log('bitcoindHost: ' + config.get('ptarmigan.apiToken'));
 
   await app.listen(3000);
 }
 bootstrap();
diff --git a/ptarmapi/src/notifications/invoices.gateway.ts b/ptarmapi/src/notifications/invoices.gateway.ts
index 193e5389..3934c3c5 100644
--- a/ptarmapi/src/notifications/invoices.gateway.ts
+++ b/ptarmapi/src/notifications/invoices.gateway.ts
@@ -6,18 +6,20 @@ import {
     WsResponse,
     OnGatewayInit,
     OnGatewayDisconnect,
 } from '@nestjs/websockets';
 import { Server, Socket } from 'ws';
-import { Logger } from '@nestjs/common';
+import { Logger, UseGuards } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
 
 @WebSocketGateway({ path: '/ws' })
 export class InvoicesGateway implements OnGatewayConnection, OnGatewayInit, OnGatewayDisconnect {
 
     private logger = new Logger('InvoicesGateway');
     clients: Socket[] = [];
 
+    @UseGuards(AuthGuard())
     @WebSocketServer()
     server: Server;
 
     afterInit(server: Server) {
         this.clients = [];
diff --git a/ptarmapi/src/ptarmigan/ptarmigan.controller.ts b/ptarmapi/src/ptarmigan/ptarmigan.controller.ts
index e29ab667..d46bb8ef 100644
--- a/ptarmapi/src/ptarmigan/ptarmigan.controller.ts
+++ b/ptarmapi/src/ptarmigan/ptarmigan.controller.ts
@@ -1,10 +1,10 @@
-import { Controller, Get, Patch, Put, Param, Post, Body, Delete, Logger, Query, Next } from '@nestjs/common';
+import { Controller, Get, Patch, Put, Param, Post, Body, Delete, Logger, Query, Next, UseGuards } from '@nestjs/common';
 import { exec, execSync } from 'child_process';
 import { PtarmiganService } from './ptarmigan.service';
 import { BitcoinService } from '../bitcoin/bitcoin.service';
-import { ApiUseTags, ApiModelProperty, ApiImplicitQuery, ApiCreatedResponse, ApiForbiddenResponse } from '@nestjs/swagger';
+import { ApiUseTags, ApiModelProperty, ApiImplicitQuery, ApiCreatedResponse, ApiForbiddenResponse, ApiBearerAuth } from '@nestjs/swagger';
 import { Validate, Matches } from 'class-validator';
 import { FeeDto } from 'src/model/fee';
 import { InvoiceDto } from 'src/model/invoice';
 import { ListInvoiceDto } from 'src/model/list-invoice';
 import { PaymentHashDto } from 'src/model/payment-hash';
@@ -19,187 +19,250 @@ import { SendPaymentDto } from 'src/model/send-payment';
 import { ListPaymentDto } from 'src/model/list-payment';
 import { AddFinalDto } from 'src/model/addfinal';
 import { ListInvoiceResponseDto } from 'src/model/list-invoice-response';
 import { CacheService } from '../cache/cache.servies'
 import { InvoicesGateway } from '../notifications/invoices.gateway';
+import { AuthGuard } from '@nestjs/passport';
 
 @ApiUseTags('ptarmigan')
 @Controller('/')
 export class PtarmiganController {
 
     constructor(
         private readonly ptarmiganService: PtarmiganService,
         private readonly bitcoinService: BitcoinService,
         private readonly cacheService: CacheService,
-        private readonly invoicesGateway: InvoicesGateway
+        private readonly invoicesGateway: InvoicesGateway,
     ) {
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('stop') // stop -> stop
     async executeStop() {
         return await this.ptarmiganService.requestTCP('stop', []);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('getinfo') // getinfo -> getinfo
     async executeGetInfo(): Promise<string> {
         return await this.ptarmiganService.requestTCP('getinfo', []);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('setfeerate') // setfeerate -> setfeerate
     async executeSetFeerate(@Body() dto: FeeDto) {
         return await this.ptarmiganService.requestTCP('setfeerate', [dto.feeratePerKw]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('estimatefundingfee') // estimatefundingfee -> dev-estimatefundingfee
     async executeEstimateFundingFee(@Body() dto: FeeDto) {
         return await this.ptarmiganService.requestTCP('estimatefundingfee', [dto.feeratePerKw]);
     }
-
+    
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('createinvoice') // createinvoice -> invoice
     async executeCreateInvoice(@Body() dto: InvoiceDto) {
         return await this.ptarmiganService.requestTCP('invoice', [dto.amountMsat, dto.minFinalCltvExpiry, dto.description, dto.invoiceExpiry]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('removeinvoice') // eraseinvoice -> removeinvoice
     async executeEraseInvoice(@Body() dto: PaymentHashDto) {
         return await this.ptarmiganService.requestTCP('eraseinvoice', [dto.paymentHash]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('removeallinvoices') // eraseinvoice -> removeallinvoices
     async executeRemoveAllInvoices() {
         return await this.ptarmiganService.requestTCP('eraseinvoice', ['']);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listinvoices') // listinvoice -> listinvoices
     async executeListInvoice(@Body() dto: ListInvoiceDto) {
         return await this.ptarmiganService.requestTCP('listinvoice', [dto.paymentHash]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('decodeinvoice') // none -> decodeinvoice
     async executeDecodeInvoice(@Body() dto: Bolt11Dto) {
         return await this.ptarmiganService.requestTCP('decodeinvoice', [dto.bolt11]);
     }
 
     // ------------------------------------------------------------------------------
     // peer
     // ------------------------------------------------------------------------------
+
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('connect') // connect -> connectpeer
     async executeConnect(@Body() dto: PeerDto) {
         return await this.ptarmiganService.requestTCP('connect', [dto.peerNodeId, dto.peerAddr, dto.peerPort]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('disconnect') // disconnect -> disconnectpeer
     async executeDisconnect(@Body() dto: PeerNodeDto) {
         return await this.ptarmiganService.requestTCP('disconnect', [dto.peerNodeId, '0.0.0.0', 0]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('getlasterror') // getlasterror -> getlasterror
     async executeGetLastErrort(@Body() dto: PeerNodeDto) {
         return await this.ptarmiganService.requestTCP('getlasterror', [dto.peerNodeId, '0.0.0.0', 0]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('dev-disautoconn') // disautoconn -> dev-disableautoconnect
     @ApiImplicitQuery({
         name: 'enable',
         enum: [0, 1],
     })
     async executeDisAutoConn(@Query('enable') enable: number) {
         return await this.ptarmiganService.requestTCP('disautoconn', [enable.toString(10)]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('dev-listtransactions') // getcommittx -> dev-listtransactions
     async executeGetCommitTx(@Body() dto: PeerNodeDto) {
         return await this.ptarmiganService.requestTCP('getcommittx', [dto.peerNodeId, '0.0.0.0', 0]);
     }
 
     // ------------------------------------------------------------------------------
     // channel
     // ------------------------------------------------------------------------------
+    
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('openchannel') // fund -> openchannel
     async executeOpenChannel(@Body() dto: FundDto) {
         return await this.ptarmiganService.commandExecuteOpenChannel(dto.peerNodeId, dto.fundingSat, dto.pushMsat, dto.feeratePerKw);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('close') // close -> closechannel
     async executeCloseChannel(@Body() dto: PeerNodeDto) {
         return await this.ptarmiganService.requestTCP('close', [dto.peerNodeId, '0.0.0.0', 0]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('forceclose') // close -> closechannel
     async executeForceCloseChannel(@Body() dto: PeerNodeDto) {
         return await this.ptarmiganService.requestTCP('close', [dto.peerNodeId, '0.0.0.0', 0, 'force']);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('dev-removechannel/:channelId') // removechannel -> dev-removechannel
     async executeRemoveChannel(@Param('channelId') channelId: string) {
         return await this.ptarmiganService.requestTCP('removechannel', [channelId]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('resetroutestate') // removechannel -> dev-removechannel
     async executeResetRouteState() {
         return await this.ptarmiganService.requestTCP('resetroutestate', []);
     }
 
     // ------------------------------------------------------------------------------
     // payment
     // ------------------------------------------------------------------------------
+   
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('sendpayment') // routepay -> sendpayment
     async executeSendPayment(@Body() dto: SendPaymentDto) {
         return await this.ptarmiganService.requestTCP('routepay', [dto.bolt11, dto.addAmountMsat]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listpayments') // listpayment -> listpayments
     async executeListPaymentsState() {
         return await this.ptarmiganService.requestTCP('listpayment', []);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listpayment') // listpayment -> listpayment=0
     async executeListPaymentState(@Body() dto: ListPaymentDto) {
         return await this.ptarmiganService.requestTCP('listpayment', [dto.listpayment]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('removepayment') // removepayment -> removepayment
     async executeRemovePaymentState(@Body() dto: PaymentIdDto) {
         return await this.ptarmiganService.requestTCP('removepayment', [dto.paymentId]);
     }
 
     // ------------------------------------------------------------------------------
     // fund
     // ------------------------------------------------------------------------------
+    
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('getwalletinfo') // getnewaddress
     async executeGetWalletInfo(): Promise<string> {
         return await this.bitcoinService.requestHTTP('getwalletinfo', []);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('getnewaddress') // getnewaddress
     async executeGetNewAddress(): Promise<string> {
         return await this.bitcoinService.requestHTTP('getnewaddress', ['', 'p2sh-segwit']);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listunspent') // listunspent
     async executeListUnspent(@Body() dto: ListUnspentDto): Promise<string> {
         return await this.bitcoinService.requestHTTP('listunspent', [dto.minconf, dto.maxconf, dto.addresses]);
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listchannels')
     async executeListChannels(): Promise<string> {
         try {
             return this.ptarmiganService.commandExecuteShowdbGetChannels().toString();
         } catch (error) {
             return 'error';
         }
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('listnodes')
     async executeListNodes(): Promise<string> {
         try {
             return this.ptarmiganService.commandExecuteShowdbListGossipNode().toString();
         } catch (error) {
             return 'error';
         }
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('getroute')
     async executeGetRoute(@Body() dto: RouteNodeDto): Promise<string> {
         try {
             return this.ptarmiganService.commandExecuteRoutingGetRoute(dto.senderNodeId, dto.receiverNodeId).toString();
         } catch (error) {
@@ -209,10 +272,12 @@ export class PtarmiganController {
 
     // ------------------------------------------------------------------------------
     // invoice notifications
     // ------------------------------------------------------------------------------
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('notification/htlcchanged') // addfinal.sh -> websocket
     async executeHtlcChangedNotification() {
         try {
             const clients = this.invoicesGateway.clients;
 
@@ -232,19 +297,21 @@ export class PtarmiganController {
                         client.send(JSON.stringify(listInvoiceReslt));
                     }
                 }
             }
         } catch (error) {
-            return 'error';
+            return error;
         }
     }
 
+    @ApiBearerAuth()
+    @UseGuards(AuthGuard())
     @Post('notification/addfinal') // addfinal.sh -> lru-cache
     async executeAddFinalNotification(@Body() dto: AddFinalDto) {
         try {
             await this.cacheService.write(dto);
         } catch (error) {
-            return 'error';
+            return error;
         }
     }
 
 }
diff --git a/ptarmapi/test/client/index.html b/ptarmapi/test/client/index.html
index c935b7ac..b31b371a 100644
--- a/ptarmapi/test/client/index.html
+++ b/ptarmapi/test/client/index.html
@@ -13,11 +13,11 @@
   <h3>status</h3>
   <div id="status">
   </div>
   <script>
     // websocket url
-    const url = 'ws://127.0.0.1:3001/ws';
+    const url = 'ws://127.0.0.1:3000/ws';
 
     document.getElementById('title').insertAdjacentHTML('afterend', '<p>url: ' + url + '</p>');
 
     //websocket connect to remote server 
     const socket = new WebSocket(url);

Vulnerable code: }
 --verbose
 --verbose

import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { Logger } from '@nestjs/common';
import { Controller, Get, Patch, Put, Param, Post, Body, Delete, Logger, Query, Next } from '@nestjs/common';
import { ApiUseTags, ApiModelProperty, ApiImplicitQuery, ApiCreatedResponse, ApiForbiddenResponse } from '@nestjs/swagger';
        private readonly invoicesGateway: InvoicesGateway

            return 'error';
            return 'error';
    const url = 'ws://127.0.0.1:3001/ws';
Fixed code: API_TOKEN="ptarmigan"
    "@nestjs/passport": "^6.1.0",
    "passport": "^0.4.0",
    "passport-http-bearer": "^1.0.1",
}
if [ "${API_TOKEN:-unknown}" == unknown ]; then
  API_TOKEN=ptarmigan
fi

 --header "Authorization: Bearer $API_TOKEN"
if [ "${API_TOKEN:-unknown}" == unknown ]; then
  API_TOKEN=ptarmigan
fi

 --header "Authorization: Bearer $API_TOKEN"
import { AuthModule } from './auth/auth.module';
    AuthModule,
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { HttpStrategy } from './http.strategy';
import { PassportModule } from '@nestjs/passport';

@Module({
 imports: [
  PassportModule.register({ defaultStrategy: 'bearer' }),
 ],
 providers: [AuthService, HttpStrategy],
 exports: [PassportModule, AuthService],
})
export class AuthModule {}
import { Injectable } from '@nestjs/common';
import { ConfigService } from 'nestjs-config';

@Injectable()
export class AuthService {

  constructor() { }

  async validateApiToken(token: string): Promise<any> {
    const apiToken = ConfigService.get('ptarmigan.apiToken');
    if (token === apiToken) {
      return true;
    }
    return false;
  }
}
import { Strategy } from 'passport-http-bearer';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class HttpStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super();
  }

  async validate(token: string) {
    const apiToken = await this.authService.validateApiToken(token);
    if (!apiToken) {
      throw new UnauthorizedException();
    }
    return apiToken;
  }
}
    apiToken: process.env.API_TOKEN || 'ptarmigan',
import { SwaggerModule, DocumentBuilder, SwaggerBaseConfig } from '@nestjs/swagger';
    .addBearerAuth('Authorization', 'header')
  Logger.log('bitcoindHost: ' + config.get('ptarmigan.apiToken'));
import { Logger, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
    @UseGuards(AuthGuard())
import { Controller, Get, Patch, Put, Param, Post, Body, Delete, Logger, Query, Next, UseGuards } from '@nestjs/common';
import { ApiUseTags, ApiModelProperty, ApiImplicitQuery, ApiCreatedResponse, ApiForbiddenResponse, ApiBearerAuth } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
        private readonly invoicesGateway: InvoicesGateway,
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())

    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
   
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
            return error;
    @ApiBearerAuth()
    @UseGuards(AuthGuard())
            return error;
    const url = 'ws://127.0.0.1:3000/ws';