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

Fix dangerous destructuration in typescript-nestjs services #20157

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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: 6 additions & 0 deletions bin/configs/typescript-nestjs-reserved-param-names.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
generatorName: typescript-nestjs
outputDir: samples/client/petstore/typescript-nestjs/builds/reservedParamNames
inputSpec: modules/openapi-generator/src/test/resources/3_0/typescript-nestjs/reserved-param-names.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-nestjs
additionalProperties:
"useSingleRequestParameter" : true
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,13 @@ export class {{classname}} {
public {{nickname}}({{#allParams}}{{^isConstEnumParam}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isConstEnumParam}}{{/allParams}}): Observable<AxiosResponse<{{#returnType}}{{{returnType}}}{{#isResponseTypeFile}}|undefined{{/isResponseTypeFile}}{{/returnType}}{{^returnType}}any{{/returnType}}>>;
public {{nickname}}({{#allParams}}{{^isConstEnumParam}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isConstEnumParam}}{{/allParams}}): Observable<any> {
{{/useSingleRequestParameter}}
{{#allParams.0}}
{{#useSingleRequestParameter}}
const {
{{#allParams}}
{{paramName}},
{{/allParams}}
} = requestParameters;

{{/useSingleRequestParameter}}
{{/allParams.0}}
{{#allParams}}
{{#required}}
{{#isConstEnumParam}}
let {{paramName}} = {{{dataType}}};
{{/isConstEnumParam}}
{{^isConstEnumParam}}
if ({{paramName}} === null || {{paramName}} === undefined) {
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} === null || {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} === undefined) {
throw new Error('Required parameter {{paramName}} was null or undefined when calling {{nickname}}.');
}

Expand All @@ -129,24 +119,24 @@ export class {{classname}} {
let queryParameters = new URLSearchParams();
{{#queryParams}}
{{#isArray}}
if ({{paramName}}) {
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}) {
{{#isCollectionFormatMulti}}
{{paramName}}.forEach((element) => {
{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}.forEach((element) => {
queryParameters.append('{{baseName}}', <any>element);
})
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
queryParameters['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']);
queryParameters['{{baseName}}'] = {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}.join(COLLECTION_FORMATS['{{collectionFormat}}']);
{{/isCollectionFormatMulti}}
}
{{/isArray}}
{{^isArray}}
if ({{paramName}} !== undefined && {{paramName}} !== null) {
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} !== undefined && {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} !== null) {
{{#isDateTime}}
queryParameters.append('{{baseName}}', (<any>{{paramName}}).toISOString());
queryParameters.append('{{baseName}}', (<any>{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}).toISOString());
{{/isDateTime}}
{{^isDateTime}}
queryParameters.append('{{baseName}}', <any>{{paramName}});
queryParameters.append('{{baseName}}', <any>{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}});
{{/isDateTime}}
}
{{/isArray}}
Expand All @@ -156,13 +146,13 @@ export class {{classname}} {
let headers = {...this.defaultHeaders};
{{#headerParams}}
{{#isArray}}
if ({{paramName}}) {
headers['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']);
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}) {
headers['{{baseName}}'] = {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}.join(COLLECTION_FORMATS['{{collectionFormat}}']);
}
{{/isArray}}
{{^isArray}}
if ({{paramName}} !== undefined && {{paramName}} !== null) {
headers['{{baseName}}'] = String({{paramName}});
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} !== undefined && {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} !== null) {
headers['{{baseName}}'] = String({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}});
}
{{/isArray}}
{{/headerParams}}
Expand Down Expand Up @@ -256,20 +246,20 @@ export class {{classname}} {
{{#formParams}}

{{#isArray}}
if ({{paramName}}) {
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}) {
{{#isCollectionFormatMulti}}
{{paramName}}.forEach((element) => {
{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}.forEach((element) => {
formParams!.append('{{baseName}}', <any>element);
})
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
formParams!.append('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']));
formParams!.append('{{baseName}}', {{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}.join(COLLECTION_FORMATS['{{collectionFormat}}']));
{{/isCollectionFormatMulti}}
}
{{/isArray}}
{{^isArray}}
if ({{paramName}} !== undefined) {
formParams!.append('{{baseName}}', <any>{{paramName}});
if ({{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}} !== undefined) {
formParams!.append('{{baseName}}', <any>{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}});
}
{{/isArray}}
{{/formParams}}
Expand All @@ -282,7 +272,7 @@ export class {{classname}} {
}

return this.httpClient.{{httpMethod}}{{^isResponseFile}}<{{#returnType}}{{{returnType}}}{{#isResponseTypeFile}}|undefined{{/isResponseTypeFile}}{{/returnType}}{{^returnType}}any{{/returnType}}>{{/isResponseFile}}(`${this.basePath}{{{path}}}`,{{#isBodyAllowed}}
{{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}{{#hasFormParams}}convertFormParamsToString ? formParams!.toString() : formParams!{{/hasFormParams}}{{^hasFormParams}}null{{/hasFormParams}}{{/bodyParam}},{{/isBodyAllowed}}
{{#bodyParam}}{{#useSingleRequestParameter}}requestParameters['{{/useSingleRequestParameter}}{{paramName}}{{#useSingleRequestParameter}}']{{/useSingleRequestParameter}}{{/bodyParam}}{{^bodyParam}}{{#hasFormParams}}convertFormParamsToString ? formParams!.toString() : formParams!{{/hasFormParams}}{{^hasFormParams}}null{{/hasFormParams}}{{/bodyParam}},{{/isBodyAllowed}}
{
{{#hasQueryParams}}
params: queryParameters,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
openapi: 3.0.0
info:
description: Test reserved param names
version: 1.0.0
title: Reserved param names
paths:
/test:
post:
security:
- bearerAuth: []
summary: Test reserved param names
description: ''
operationId: testReservedParamNames
parameters:
- name: from
in: query
description: Might conflict with rxjs import
required: true
schema:
type: string
- name: headers
in: header
description: Might conflict with headers const
required: true
schema:
type: string
responses:
'200':
description: successful operation
'405':
description: Invalid input
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
wwwroot/*.js
node_modules
typings
dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.gitignore
README.md
api.module.ts
api/api.ts
api/default.service.ts
configuration.ts
git_push.sh
index.ts
model/models.ts
variables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.11.0-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
## @

### Building

To install the required dependencies and to build the typescript sources run:
```
npm install
npm run build
```

#### General usage

In your Nestjs project:


```
// without configuring providers
import { ApiModule } from '';
import { HttpModule } from '@nestjs/axios';

@Module({
imports: [
ApiModule,
HttpModule
],
providers: []
})
export class AppModule {}
```

```
// configuring providers
import { ApiModule, Configuration, ConfigurationParameters } from '';

export function apiConfigFactory (): Configuration => {
const params: ConfigurationParameters = {
// set configuration parameters here.
}
return new Configuration(params);
}

@Module({
imports: [ ApiModule.forRoot(apiConfigFactory) ],
declarations: [ AppComponent ],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```

```
import { DefaultApi } from '';

export class AppComponent {
constructor(private apiGateway: DefaultApi) { }
}
```

Note: The ApiModule a dynamic module and instantiated once app wide.
This is to ensure that all services are treated as singletons.

#### Using multiple swagger files / APIs / ApiModules
In order to use multiple `ApiModules` generated from different swagger files,
you can create an alias name when importing the modules
in order to avoid naming conflicts:
```
import { ApiModule } from 'my-api-path';
import { ApiModule as OtherApiModule } from 'my-other-api-path';
import { HttpModule } from '@nestjs/axios';

@Module({
imports: [
ApiModule,
OtherApiModule,
HttpModule
]
})
export class AppModule {

}
```


### Set service base path
If different than the generated base path, during app bootstrap, you can provide the base path to your service.

```
import { BASE_PATH } from '';

bootstrap(AppComponent, [
{ provide: BASE_PATH, useValue: 'https://your-web-service.com' },
]);
```
or

```
import { BASE_PATH } from '';

@Module({
imports: [],
declarations: [ AppComponent ],
providers: [ provide: BASE_PATH, useValue: 'https://your-web-service.com' ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```

### Configuring the module with `forRootAsync`

You can also use the Nestjs Config Module/Service to configure your app with `forRootAsync`.

```
@Module({
imports: [
ApiModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService): Configuration => {
const params: ConfigurationParameters = {
// set configuration parameters here.
basePath: config.get('API_URL'),
};
return new Configuration(params);
},
})
],
declarations: [ AppComponent ],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```

#### Using @nestjs/cli
First extend your `src/environments/*.ts` files by adding the corresponding base path:

```
export const environment = {
production: false,
API_BASE_PATH: 'http://127.0.0.1:8080'
};
```

In the src/app/app.module.ts:
```
import { BASE_PATH } from '';
import { environment } from '../environments/environment';

@Module({
declarations: [
AppComponent
],
imports: [ ],
providers: [
{
provide: 'BASE_PATH',
useValue: environment.API_BASE_PATH
}
]
})
export class AppModule { }
```
Loading
Loading