@sasjs/adapter 3.10.8 → 3.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SASjs.d.ts +0 -14
- package/index.js +2 -2
- package/node/SASjs.d.ts +0 -14
- package/node/index.js +2 -2
- package/node/types/errors/InvalidSASjsCsrfError.d.ts +3 -0
- package/node/utils/index.d.ts +7 -6
- package/node/utils/validateInput.d.ts +14 -0
- package/package.json +1 -1
- package/src/SASjs.ts +2 -70
- package/src/auth/AuthManager.ts +10 -2
- package/src/request/RequestClient.ts +16 -5
- package/src/types/errors/InvalidSASjsCsrfError.ts +9 -0
- package/src/utils/index.ts +7 -6
- package/src/utils/validateInput.ts +90 -0
- package/types/errors/InvalidSASjsCsrfError.d.ts +3 -0
- package/utils/index.d.ts +7 -6
- package/utils/{convertToCsv.spec.d.ts → spec/convertToCsv.spec.d.ts} +0 -0
- package/utils/spec/validateInput.spec.d.ts +1 -0
- package/utils/validateInput.d.ts +14 -0
- package/node/types/errors/InvalidCsrfError.d.ts +0 -3
- package/src/types/errors/InvalidCsrfError.ts +0 -9
- package/types/errors/InvalidCsrfError.d.ts +0 -3
package/node/utils/index.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes';
|
|
1
2
|
export * from './asyncForEach';
|
|
2
3
|
export * from './compareTimestamps';
|
|
3
4
|
export * from './convertToCsv';
|
|
4
5
|
export * from './createAxiosInstance';
|
|
5
6
|
export * from './delay';
|
|
7
|
+
export * from './fetchLogByChunks';
|
|
8
|
+
export * from './getValidJson';
|
|
6
9
|
export * from './isNode';
|
|
7
10
|
export * from './isRelativePath';
|
|
8
11
|
export * from './isUri';
|
|
9
12
|
export * from './isUrl';
|
|
10
13
|
export * from './needsRetry';
|
|
11
14
|
export * from './parseGeneratedCode';
|
|
12
|
-
export * from './parseSourceCode';
|
|
13
15
|
export * from './parseSasViyaLog';
|
|
16
|
+
export * from './parseSourceCode';
|
|
17
|
+
export * from './parseViyaDebugResponse';
|
|
18
|
+
export * from './parseWeboutResponse';
|
|
14
19
|
export * from './serialize';
|
|
15
20
|
export * from './splitChunks';
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks';
|
|
18
|
-
export * from './getValidJson';
|
|
19
|
-
export * from './parseViyaDebugResponse';
|
|
20
|
-
export * from './appendExtraResponseAttributes';
|
|
21
|
+
export * from './validateInput';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MORE_INFO = "For more info see https://sasjs.io/sasjs-adapter/#request-response";
|
|
2
|
+
export declare const INVALID_TABLE_STRUCTURE: string;
|
|
3
|
+
/**
|
|
4
|
+
* This function validates the input data structure and table naming convention
|
|
5
|
+
*
|
|
6
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
7
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateInput: (data: {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
} | null) => {
|
|
12
|
+
status: boolean;
|
|
13
|
+
msg: string;
|
|
14
|
+
};
|
package/package.json
CHANGED
package/src/SASjs.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compareTimestamps, asyncForEach } from './utils'
|
|
1
|
+
import { compareTimestamps, asyncForEach, validateInput } from './utils'
|
|
2
2
|
import {
|
|
3
3
|
SASjsConfig,
|
|
4
4
|
UploadFile,
|
|
@@ -686,7 +686,7 @@ export default class SASjs {
|
|
|
686
686
|
...config
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
-
const validationResult =
|
|
689
|
+
const validationResult = validateInput(data)
|
|
690
690
|
|
|
691
691
|
// status is true if the data passes validation checks above
|
|
692
692
|
if (validationResult.status) {
|
|
@@ -748,74 +748,6 @@ export default class SASjs {
|
|
|
748
748
|
}
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
-
/**
|
|
752
|
-
* This function validates the input data structure and table naming convention
|
|
753
|
-
*
|
|
754
|
-
* @param data A json object that contains one or more tables, it can also be null
|
|
755
|
-
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
756
|
-
*/
|
|
757
|
-
private validateInput(data: { [key: string]: any } | null): {
|
|
758
|
-
status: boolean
|
|
759
|
-
msg: string
|
|
760
|
-
} {
|
|
761
|
-
if (data === null) return { status: true, msg: '' }
|
|
762
|
-
|
|
763
|
-
const isSasFormatsTable = (key: string) =>
|
|
764
|
-
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
765
|
-
|
|
766
|
-
for (const key in data) {
|
|
767
|
-
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
768
|
-
return {
|
|
769
|
-
status: false,
|
|
770
|
-
msg: 'First letter of table should be alphabet or underscore.'
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
775
|
-
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
if (key.length > 32) {
|
|
779
|
-
return {
|
|
780
|
-
status: false,
|
|
781
|
-
msg: 'Maximum length for table name could be 32 characters.'
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
if (this.getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
786
|
-
return {
|
|
787
|
-
status: false,
|
|
788
|
-
msg: 'Parameter data contains invalid table structure.'
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
for (let i = 0; i < data[key].length; i++) {
|
|
793
|
-
if (this.getType(data[key][i]) !== 'object') {
|
|
794
|
-
return {
|
|
795
|
-
status: false,
|
|
796
|
-
msg: `Table ${key} contains invalid structure.`
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return { status: true, msg: '' }
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* this function returns the type of variable
|
|
807
|
-
*
|
|
808
|
-
* @param data it could be anything, like string, array, object etc.
|
|
809
|
-
* @returns a string which tells the type of input parameter
|
|
810
|
-
*/
|
|
811
|
-
private getType(data: any): string {
|
|
812
|
-
if (Array.isArray(data)) {
|
|
813
|
-
return 'Array'
|
|
814
|
-
} else {
|
|
815
|
-
return typeof data
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
751
|
/**
|
|
820
752
|
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
|
821
753
|
* @param serviceJson - the JSON specifying the folders and services to be created.
|
package/src/auth/AuthManager.ts
CHANGED
|
@@ -223,9 +223,17 @@ export class AuthManager {
|
|
|
223
223
|
|
|
224
224
|
private async getNewLoginForm() {
|
|
225
225
|
if (this.serverType === ServerType.Sasjs) {
|
|
226
|
-
// server will be sending CSRF
|
|
226
|
+
// server will be sending CSRF token in response,
|
|
227
|
+
// need to save in cookie so that,
|
|
227
228
|
// http client will use it automatically
|
|
228
|
-
return this.requestClient.get('/', undefined)
|
|
229
|
+
return this.requestClient.get('/', undefined).then(({ result }) => {
|
|
230
|
+
const cookie =
|
|
231
|
+
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
|
|
232
|
+
result as string
|
|
233
|
+
)?.[1]
|
|
234
|
+
|
|
235
|
+
if (cookie) document.cookie = cookie
|
|
236
|
+
})
|
|
229
237
|
}
|
|
230
238
|
|
|
231
239
|
const { result: formResponse } = await this.requestClient.get<string>(
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
parseSourceCode,
|
|
20
20
|
createAxiosInstance
|
|
21
21
|
} from '../utils'
|
|
22
|
-
import {
|
|
22
|
+
import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
|
|
23
23
|
|
|
24
24
|
export interface HttpClient {
|
|
25
25
|
get<T>(
|
|
@@ -499,12 +499,20 @@ export class RequestClient implements HttpClient {
|
|
|
499
499
|
throw e
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
-
if (e instanceof
|
|
503
|
-
// Fetching root
|
|
502
|
+
if (e instanceof InvalidSASjsCsrfError) {
|
|
503
|
+
// Fetching root and creating CSRF cookie
|
|
504
504
|
await this.httpClient
|
|
505
505
|
.get('/', {
|
|
506
506
|
withCredentials: true
|
|
507
507
|
})
|
|
508
|
+
.then((response) => {
|
|
509
|
+
const cookie =
|
|
510
|
+
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
|
|
511
|
+
response.data
|
|
512
|
+
)?.[1]
|
|
513
|
+
|
|
514
|
+
if (cookie) document.cookie = cookie
|
|
515
|
+
})
|
|
508
516
|
.catch((err) => {
|
|
509
517
|
throw prefixMessage(err, 'Error while re-fetching CSRF token.')
|
|
510
518
|
})
|
|
@@ -611,8 +619,11 @@ export const throwIfError = (response: AxiosResponse) => {
|
|
|
611
619
|
throw new LoginRequiredError(response.data)
|
|
612
620
|
}
|
|
613
621
|
|
|
614
|
-
if (
|
|
615
|
-
|
|
622
|
+
if (
|
|
623
|
+
typeof response.data === 'string' &&
|
|
624
|
+
response.data.toLowerCase() === 'invalid csrf token!'
|
|
625
|
+
) {
|
|
626
|
+
throw new InvalidSASjsCsrfError()
|
|
616
627
|
}
|
|
617
628
|
break
|
|
618
629
|
case 401:
|
package/src/utils/index.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes'
|
|
1
2
|
export * from './asyncForEach'
|
|
2
3
|
export * from './compareTimestamps'
|
|
3
4
|
export * from './convertToCsv'
|
|
4
5
|
export * from './createAxiosInstance'
|
|
5
6
|
export * from './delay'
|
|
7
|
+
export * from './fetchLogByChunks'
|
|
8
|
+
export * from './getValidJson'
|
|
6
9
|
export * from './isNode'
|
|
7
10
|
export * from './isRelativePath'
|
|
8
11
|
export * from './isUri'
|
|
9
12
|
export * from './isUrl'
|
|
10
13
|
export * from './needsRetry'
|
|
11
14
|
export * from './parseGeneratedCode'
|
|
12
|
-
export * from './parseSourceCode'
|
|
13
15
|
export * from './parseSasViyaLog'
|
|
16
|
+
export * from './parseSourceCode'
|
|
17
|
+
export * from './parseViyaDebugResponse'
|
|
18
|
+
export * from './parseWeboutResponse'
|
|
14
19
|
export * from './serialize'
|
|
15
20
|
export * from './splitChunks'
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks'
|
|
18
|
-
export * from './getValidJson'
|
|
19
|
-
export * from './parseViyaDebugResponse'
|
|
20
|
-
export * from './appendExtraResponseAttributes'
|
|
21
|
+
export * from './validateInput'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export const MORE_INFO =
|
|
2
|
+
'For more info see https://sasjs.io/sasjs-adapter/#request-response'
|
|
3
|
+
export const INVALID_TABLE_STRUCTURE = `Parameter data contains invalid table structure. ${MORE_INFO}`
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This function validates the input data structure and table naming convention
|
|
7
|
+
*
|
|
8
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
9
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
10
|
+
*/
|
|
11
|
+
export const validateInput = (
|
|
12
|
+
data: { [key: string]: any } | null
|
|
13
|
+
): {
|
|
14
|
+
status: boolean
|
|
15
|
+
msg: string
|
|
16
|
+
} => {
|
|
17
|
+
if (data === null) return { status: true, msg: '' }
|
|
18
|
+
|
|
19
|
+
if (getType(data) !== 'object') {
|
|
20
|
+
return {
|
|
21
|
+
status: false,
|
|
22
|
+
msg: INVALID_TABLE_STRUCTURE
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isSasFormatsTable = (key: string) =>
|
|
27
|
+
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
28
|
+
|
|
29
|
+
for (const key in data) {
|
|
30
|
+
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
31
|
+
return {
|
|
32
|
+
status: false,
|
|
33
|
+
msg: 'First letter of table should be alphabet or underscore.'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
38
|
+
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (key.length > 32) {
|
|
42
|
+
return {
|
|
43
|
+
status: false,
|
|
44
|
+
msg: 'Maximum length for table name could be 32 characters.'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
49
|
+
return {
|
|
50
|
+
status: false,
|
|
51
|
+
msg: INVALID_TABLE_STRUCTURE
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const item of data[key]) {
|
|
56
|
+
if (getType(item) !== 'object') {
|
|
57
|
+
return {
|
|
58
|
+
status: false,
|
|
59
|
+
msg: `Table ${key} contains invalid structure. ${MORE_INFO}`
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
const attributes = Object.keys(item)
|
|
63
|
+
for (const attribute of attributes) {
|
|
64
|
+
if (item[attribute] === undefined) {
|
|
65
|
+
return {
|
|
66
|
+
status: false,
|
|
67
|
+
msg: `A row in table ${key} contains invalid value. Can't assign undefined to ${attribute}.`
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { status: true, msg: '' }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* this function returns the type of variable
|
|
80
|
+
*
|
|
81
|
+
* @param data it could be anything, like string, array, object etc.
|
|
82
|
+
* @returns a string which tells the type of input parameter
|
|
83
|
+
*/
|
|
84
|
+
const getType = (data: any): string => {
|
|
85
|
+
if (Array.isArray(data)) {
|
|
86
|
+
return 'Array'
|
|
87
|
+
} else {
|
|
88
|
+
return typeof data
|
|
89
|
+
}
|
|
90
|
+
}
|
package/utils/index.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes';
|
|
1
2
|
export * from './asyncForEach';
|
|
2
3
|
export * from './compareTimestamps';
|
|
3
4
|
export * from './convertToCsv';
|
|
4
5
|
export * from './createAxiosInstance';
|
|
5
6
|
export * from './delay';
|
|
7
|
+
export * from './fetchLogByChunks';
|
|
8
|
+
export * from './getValidJson';
|
|
6
9
|
export * from './isNode';
|
|
7
10
|
export * from './isRelativePath';
|
|
8
11
|
export * from './isUri';
|
|
9
12
|
export * from './isUrl';
|
|
10
13
|
export * from './needsRetry';
|
|
11
14
|
export * from './parseGeneratedCode';
|
|
12
|
-
export * from './parseSourceCode';
|
|
13
15
|
export * from './parseSasViyaLog';
|
|
16
|
+
export * from './parseSourceCode';
|
|
17
|
+
export * from './parseViyaDebugResponse';
|
|
18
|
+
export * from './parseWeboutResponse';
|
|
14
19
|
export * from './serialize';
|
|
15
20
|
export * from './splitChunks';
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks';
|
|
18
|
-
export * from './getValidJson';
|
|
19
|
-
export * from './parseViyaDebugResponse';
|
|
20
|
-
export * from './appendExtraResponseAttributes';
|
|
21
|
+
export * from './validateInput';
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MORE_INFO = "For more info see https://sasjs.io/sasjs-adapter/#request-response";
|
|
2
|
+
export declare const INVALID_TABLE_STRUCTURE: string;
|
|
3
|
+
/**
|
|
4
|
+
* This function validates the input data structure and table naming convention
|
|
5
|
+
*
|
|
6
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
7
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateInput: (data: {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
} | null) => {
|
|
12
|
+
status: boolean;
|
|
13
|
+
msg: string;
|
|
14
|
+
};
|