paper-search-cli 0.1.0
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/.env.example +165 -0
- package/LICENSE +21 -0
- package/README-sc.md +642 -0
- package/README.md +642 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +637 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/ConfigService.d.ts +26 -0
- package/dist/config/ConfigService.d.ts.map +1 -0
- package/dist/config/ConfigService.js +145 -0
- package/dist/config/ConfigService.js.map +1 -0
- package/dist/config/constants.d.ts +140 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +93 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/core/diagnostics.d.ts +43 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +544 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/handleToolCall.d.ts +8 -0
- package/dist/core/handleToolCall.d.ts.map +1 -0
- package/dist/core/handleToolCall.js +440 -0
- package/dist/core/handleToolCall.js.map +1 -0
- package/dist/core/schemas.d.ts +454 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +322 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/searchers.d.ts +45 -0
- package/dist/core/searchers.d.ts.map +1 -0
- package/dist/core/searchers.js +73 -0
- package/dist/core/searchers.js.map +1 -0
- package/dist/core/tools.d.ts +7 -0
- package/dist/core/tools.d.ts.map +1 -0
- package/dist/core/tools.js +640 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/models/Paper.d.ts +64 -0
- package/dist/models/Paper.d.ts.map +1 -0
- package/dist/models/Paper.js +70 -0
- package/dist/models/Paper.js.map +1 -0
- package/dist/platforms/ArxivSearcher.d.ts +64 -0
- package/dist/platforms/ArxivSearcher.d.ts.map +1 -0
- package/dist/platforms/ArxivSearcher.js +531 -0
- package/dist/platforms/ArxivSearcher.js.map +1 -0
- package/dist/platforms/BioRxivSearcher.d.ts +47 -0
- package/dist/platforms/BioRxivSearcher.d.ts.map +1 -0
- package/dist/platforms/BioRxivSearcher.js +196 -0
- package/dist/platforms/BioRxivSearcher.js.map +1 -0
- package/dist/platforms/CORESearcher.d.ts +16 -0
- package/dist/platforms/CORESearcher.d.ts.map +1 -0
- package/dist/platforms/CORESearcher.js +148 -0
- package/dist/platforms/CORESearcher.js.map +1 -0
- package/dist/platforms/CrossrefSearcher.d.ts +34 -0
- package/dist/platforms/CrossrefSearcher.d.ts.map +1 -0
- package/dist/platforms/CrossrefSearcher.js +339 -0
- package/dist/platforms/CrossrefSearcher.js.map +1 -0
- package/dist/platforms/EuropePMCSearcher.d.ts +20 -0
- package/dist/platforms/EuropePMCSearcher.d.ts.map +1 -0
- package/dist/platforms/EuropePMCSearcher.js +173 -0
- package/dist/platforms/EuropePMCSearcher.js.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts +77 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.js +262 -0
- package/dist/platforms/GoogleScholarSearcher.js.map +1 -0
- package/dist/platforms/IACRSearcher.d.ts +51 -0
- package/dist/platforms/IACRSearcher.d.ts.map +1 -0
- package/dist/platforms/IACRSearcher.js +339 -0
- package/dist/platforms/IACRSearcher.js.map +1 -0
- package/dist/platforms/OpenAIRESearcher.d.ts +22 -0
- package/dist/platforms/OpenAIRESearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAIRESearcher.js +223 -0
- package/dist/platforms/OpenAIRESearcher.js.map +1 -0
- package/dist/platforms/OpenAlexSearcher.d.ts +14 -0
- package/dist/platforms/OpenAlexSearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAlexSearcher.js +114 -0
- package/dist/platforms/OpenAlexSearcher.js.map +1 -0
- package/dist/platforms/PMCSearcher.d.ts +20 -0
- package/dist/platforms/PMCSearcher.d.ts.map +1 -0
- package/dist/platforms/PMCSearcher.js +177 -0
- package/dist/platforms/PMCSearcher.js.map +1 -0
- package/dist/platforms/PaperSource.d.ts +143 -0
- package/dist/platforms/PaperSource.d.ts.map +1 -0
- package/dist/platforms/PaperSource.js +125 -0
- package/dist/platforms/PaperSource.js.map +1 -0
- package/dist/platforms/PubMedSearcher.d.ts +104 -0
- package/dist/platforms/PubMedSearcher.d.ts.map +1 -0
- package/dist/platforms/PubMedSearcher.js +422 -0
- package/dist/platforms/PubMedSearcher.js.map +1 -0
- package/dist/platforms/SciHubSearcher.d.ts +66 -0
- package/dist/platforms/SciHubSearcher.d.ts.map +1 -0
- package/dist/platforms/SciHubSearcher.js +398 -0
- package/dist/platforms/SciHubSearcher.js.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts +42 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.js +326 -0
- package/dist/platforms/ScienceDirectSearcher.js.map +1 -0
- package/dist/platforms/ScopusSearcher.d.ts +43 -0
- package/dist/platforms/ScopusSearcher.d.ts.map +1 -0
- package/dist/platforms/ScopusSearcher.js +364 -0
- package/dist/platforms/ScopusSearcher.js.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts +96 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.js +419 -0
- package/dist/platforms/SemanticScholarSearcher.js.map +1 -0
- package/dist/platforms/SpringerSearcher.d.ts +54 -0
- package/dist/platforms/SpringerSearcher.d.ts.map +1 -0
- package/dist/platforms/SpringerSearcher.js +407 -0
- package/dist/platforms/SpringerSearcher.js.map +1 -0
- package/dist/platforms/UnpaywallSearcher.d.ts +18 -0
- package/dist/platforms/UnpaywallSearcher.d.ts.map +1 -0
- package/dist/platforms/UnpaywallSearcher.js +115 -0
- package/dist/platforms/UnpaywallSearcher.js.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts +111 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.js +500 -0
- package/dist/platforms/WebOfScienceSearcher.js.map +1 -0
- package/dist/platforms/WileySearcher.d.ts +44 -0
- package/dist/platforms/WileySearcher.d.ts.map +1 -0
- package/dist/platforms/WileySearcher.js +148 -0
- package/dist/platforms/WileySearcher.js.map +1 -0
- package/dist/services/CitationService.d.ts +66 -0
- package/dist/services/CitationService.d.ts.map +1 -0
- package/dist/services/CitationService.js +237 -0
- package/dist/services/CitationService.js.map +1 -0
- package/dist/services/MultiSourceSearchService.d.ts +19 -0
- package/dist/services/MultiSourceSearchService.d.ts.map +1 -0
- package/dist/services/MultiSourceSearchService.js +96 -0
- package/dist/services/MultiSourceSearchService.js.map +1 -0
- package/dist/services/OpenAccessFallbackService.d.ts +20 -0
- package/dist/services/OpenAccessFallbackService.d.ts.map +1 -0
- package/dist/services/OpenAccessFallbackService.js +124 -0
- package/dist/services/OpenAccessFallbackService.js.map +1 -0
- package/dist/utils/ErrorHandler.d.ts +99 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +266 -0
- package/dist/utils/ErrorHandler.js.map +1 -0
- package/dist/utils/Logger.d.ts +6 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +26 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/PDFExtractor.d.ts +34 -0
- package/dist/utils/PDFExtractor.d.ts.map +1 -0
- package/dist/utils/PDFExtractor.js +130 -0
- package/dist/utils/PDFExtractor.js.map +1 -0
- package/dist/utils/PdfDownload.d.ts +7 -0
- package/dist/utils/PdfDownload.d.ts.map +1 -0
- package/dist/utils/PdfDownload.js +52 -0
- package/dist/utils/PdfDownload.js.map +1 -0
- package/dist/utils/QuotaManager.d.ts +32 -0
- package/dist/utils/QuotaManager.d.ts.map +1 -0
- package/dist/utils/QuotaManager.js +95 -0
- package/dist/utils/QuotaManager.js.map +1 -0
- package/dist/utils/RateLimiter.d.ts +50 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +121 -0
- package/dist/utils/RateLimiter.js.map +1 -0
- package/dist/utils/RequestCache.d.ts +26 -0
- package/dist/utils/RequestCache.d.ts.map +1 -0
- package/dist/utils/RequestCache.js +66 -0
- package/dist/utils/RequestCache.js.map +1 -0
- package/dist/utils/SecurityUtils.d.ts +80 -0
- package/dist/utils/SecurityUtils.d.ts.map +1 -0
- package/dist/utils/SecurityUtils.js +357 -0
- package/dist/utils/SecurityUtils.js.map +1 -0
- package/package.json +111 -0
- package/skills/paper-search/SKILL.md +192 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for sanitizing and validating data
|
|
3
|
+
* Provides comprehensive protection against security vulnerabilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Comprehensive request sanitization to remove sensitive data
|
|
7
|
+
* @param config - Axios request configuration
|
|
8
|
+
* @returns Sanitized configuration copy
|
|
9
|
+
*/
|
|
10
|
+
export declare function sanitizeRequest(config: any): any;
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize headers to remove sensitive information
|
|
13
|
+
*/
|
|
14
|
+
export declare function sanitizeHeaders(headers: Record<string, any>): Record<string, any>;
|
|
15
|
+
/**
|
|
16
|
+
* Sanitize URL parameters
|
|
17
|
+
*/
|
|
18
|
+
export declare function sanitizeParams(params: Record<string, any>): Record<string, any>;
|
|
19
|
+
/**
|
|
20
|
+
* Sanitize request body
|
|
21
|
+
*/
|
|
22
|
+
export declare function sanitizeBody(body: any): any;
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize URL to remove sensitive query parameters
|
|
25
|
+
*/
|
|
26
|
+
export declare function sanitizeUrl(url: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Validate and sanitize a DOI string
|
|
29
|
+
*/
|
|
30
|
+
export declare function sanitizeDoi(doi: string): {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
sanitized: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Escape query value for different contexts
|
|
37
|
+
*/
|
|
38
|
+
export declare function escapeQueryValue(value: string, context?: 'springer' | 'wos' | 'general'): string;
|
|
39
|
+
/**
|
|
40
|
+
* Validate query complexity to prevent DoS
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateQueryComplexity(query: string, options?: {
|
|
43
|
+
maxLength?: number;
|
|
44
|
+
maxBooleanOperators?: number;
|
|
45
|
+
}): {
|
|
46
|
+
valid: boolean;
|
|
47
|
+
error?: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Create a timeout wrapper for promises
|
|
51
|
+
*/
|
|
52
|
+
export declare function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Generate a correlation ID for request tracking
|
|
55
|
+
*/
|
|
56
|
+
export declare function generateCorrelationId(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Mask sensitive data in strings
|
|
59
|
+
*/
|
|
60
|
+
export declare function maskSensitiveData(str: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Check if a string looks like an API key or token
|
|
63
|
+
*/
|
|
64
|
+
export declare function looksLikeToken(str: string): boolean;
|
|
65
|
+
declare const _default: {
|
|
66
|
+
sanitizeRequest: typeof sanitizeRequest;
|
|
67
|
+
sanitizeHeaders: typeof sanitizeHeaders;
|
|
68
|
+
sanitizeParams: typeof sanitizeParams;
|
|
69
|
+
sanitizeBody: typeof sanitizeBody;
|
|
70
|
+
sanitizeUrl: typeof sanitizeUrl;
|
|
71
|
+
sanitizeDoi: typeof sanitizeDoi;
|
|
72
|
+
escapeQueryValue: typeof escapeQueryValue;
|
|
73
|
+
validateQueryComplexity: typeof validateQueryComplexity;
|
|
74
|
+
withTimeout: typeof withTimeout;
|
|
75
|
+
generateCorrelationId: typeof generateCorrelationId;
|
|
76
|
+
maskSensitiveData: typeof maskSensitiveData;
|
|
77
|
+
looksLikeToken: typeof looksLikeToken;
|
|
78
|
+
};
|
|
79
|
+
export default _default;
|
|
80
|
+
//# sourceMappingURL=SecurityUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityUtils.d.ts","sourceRoot":"","sources":["../../src/utils/SecurityUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAiChD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA0CjF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA2B/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAwC3C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA2B/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA6C9F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,UAAU,GAAG,KAAK,GAAG,SAAqB,GAClD,MAAM,CAsCR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA0CpC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAanD;;;;;;;;;;;;;;;AAED,wBAaE"}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for sanitizing and validating data
|
|
3
|
+
* Provides comprehensive protection against security vulnerabilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Comprehensive request sanitization to remove sensitive data
|
|
7
|
+
* @param config - Axios request configuration
|
|
8
|
+
* @returns Sanitized configuration copy
|
|
9
|
+
*/
|
|
10
|
+
export function sanitizeRequest(config) {
|
|
11
|
+
if (!config)
|
|
12
|
+
return config;
|
|
13
|
+
// Deep clone to avoid mutating original
|
|
14
|
+
let sanitized;
|
|
15
|
+
try {
|
|
16
|
+
sanitized = JSON.parse(JSON.stringify(config));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// If JSON serialization fails, return redacted version
|
|
20
|
+
return { __redacted: 'Failed to sanitize - potentially circular reference' };
|
|
21
|
+
}
|
|
22
|
+
// Sanitize headers
|
|
23
|
+
if (sanitized.headers) {
|
|
24
|
+
sanitized.headers = sanitizeHeaders(sanitized.headers);
|
|
25
|
+
}
|
|
26
|
+
// Sanitize URL parameters
|
|
27
|
+
if (sanitized.params) {
|
|
28
|
+
sanitized.params = sanitizeParams(sanitized.params);
|
|
29
|
+
}
|
|
30
|
+
// Sanitize request body
|
|
31
|
+
if (sanitized.data) {
|
|
32
|
+
sanitized.data = sanitizeBody(sanitized.data);
|
|
33
|
+
}
|
|
34
|
+
// Sanitize URL
|
|
35
|
+
if (sanitized.url) {
|
|
36
|
+
sanitized.url = sanitizeUrl(sanitized.url);
|
|
37
|
+
}
|
|
38
|
+
return sanitized;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sanitize headers to remove sensitive information
|
|
42
|
+
*/
|
|
43
|
+
export function sanitizeHeaders(headers) {
|
|
44
|
+
if (!headers)
|
|
45
|
+
return headers;
|
|
46
|
+
const sanitized = { ...headers };
|
|
47
|
+
// Patterns for sensitive headers (case-insensitive)
|
|
48
|
+
const sensitivePatterns = [
|
|
49
|
+
/^api[-_]?key$/i,
|
|
50
|
+
/^x[-_]api[-_]key$/i,
|
|
51
|
+
/^authorization$/i,
|
|
52
|
+
/^x[-_]apikey$/i,
|
|
53
|
+
/^access[-_]token$/i,
|
|
54
|
+
/^bearer$/i,
|
|
55
|
+
/^x[-_]auth[-_]token$/i,
|
|
56
|
+
/^cookie$/i,
|
|
57
|
+
/^set[-_]cookie$/i,
|
|
58
|
+
/^x[-_]csrf[-_]token$/i,
|
|
59
|
+
/^x[-_]forwarded[-_]for$/i, // May contain IP
|
|
60
|
+
/^referer$/i, // May contain sensitive URLs
|
|
61
|
+
/^user[-_]agent$/i // May contain system info
|
|
62
|
+
];
|
|
63
|
+
Object.keys(sanitized).forEach(key => {
|
|
64
|
+
const lowerKey = key.toLowerCase();
|
|
65
|
+
// Check against patterns
|
|
66
|
+
if (sensitivePatterns.some(pattern => pattern.test(key))) {
|
|
67
|
+
sanitized[key] = '***REDACTED***';
|
|
68
|
+
}
|
|
69
|
+
// Also check values that might contain tokens
|
|
70
|
+
if (typeof sanitized[key] === 'string') {
|
|
71
|
+
if (sanitized[key].match(/^(Bearer|Basic)\s+/i) ||
|
|
72
|
+
sanitized[key].match(/^[a-zA-Z0-9_-]{20,}$/) || // Likely token
|
|
73
|
+
sanitized[key].includes('session=') ||
|
|
74
|
+
sanitized[key].includes('token=')) {
|
|
75
|
+
sanitized[key] = '***REDACTED***';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return sanitized;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Sanitize URL parameters
|
|
83
|
+
*/
|
|
84
|
+
export function sanitizeParams(params) {
|
|
85
|
+
if (!params)
|
|
86
|
+
return params;
|
|
87
|
+
const sanitized = { ...params };
|
|
88
|
+
Object.keys(sanitized).forEach(key => {
|
|
89
|
+
const lowerKey = key.toLowerCase();
|
|
90
|
+
// Check for common sensitive parameter names
|
|
91
|
+
if (lowerKey.includes('api_key') ||
|
|
92
|
+
lowerKey.includes('apikey') ||
|
|
93
|
+
lowerKey.includes('token') ||
|
|
94
|
+
lowerKey.includes('secret') ||
|
|
95
|
+
lowerKey.includes('password') ||
|
|
96
|
+
lowerKey.includes('private') ||
|
|
97
|
+
lowerKey.includes('auth')) {
|
|
98
|
+
sanitized[key] = '***REDACTED***';
|
|
99
|
+
}
|
|
100
|
+
// Mask values that look like tokens
|
|
101
|
+
if (typeof sanitized[key] === 'string' &&
|
|
102
|
+
sanitized[key].match(/^[a-zA-Z0-9_-]{16,}$/)) {
|
|
103
|
+
sanitized[key] = sanitized[key].substring(0, 4) + '***';
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return sanitized;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Sanitize request body
|
|
110
|
+
*/
|
|
111
|
+
export function sanitizeBody(body) {
|
|
112
|
+
if (!body)
|
|
113
|
+
return body;
|
|
114
|
+
// For objects, recursively sanitize
|
|
115
|
+
if (typeof body === 'object' && body !== null) {
|
|
116
|
+
// Handle arrays
|
|
117
|
+
if (Array.isArray(body)) {
|
|
118
|
+
return body.map(item => sanitizeBody(item));
|
|
119
|
+
}
|
|
120
|
+
// Handle objects
|
|
121
|
+
const sanitized = {};
|
|
122
|
+
for (const [key, value] of Object.entries(body)) {
|
|
123
|
+
const lowerKey = key.toLowerCase();
|
|
124
|
+
// Check for sensitive keys
|
|
125
|
+
if (lowerKey.includes('password') ||
|
|
126
|
+
lowerKey.includes('secret') ||
|
|
127
|
+
lowerKey.includes('token') ||
|
|
128
|
+
lowerKey.includes('api_key') ||
|
|
129
|
+
lowerKey.includes('private')) {
|
|
130
|
+
sanitized[key] = '***REDACTED***';
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
sanitized[key] = sanitizeBody(value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return sanitized;
|
|
137
|
+
}
|
|
138
|
+
// For strings, check if it looks like a token
|
|
139
|
+
if (typeof body === 'string') {
|
|
140
|
+
if (body.match(/^(Bearer|Basic)\s+/i)) {
|
|
141
|
+
return body.replace(/\s+\S+/, ' ***REDACTED***');
|
|
142
|
+
}
|
|
143
|
+
if (body.match(/^[a-zA-Z0-9_-]{32,}$/)) {
|
|
144
|
+
return body.substring(0, 8) + '***';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return body;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Sanitize URL to remove sensitive query parameters
|
|
151
|
+
*/
|
|
152
|
+
export function sanitizeUrl(url) {
|
|
153
|
+
if (!url)
|
|
154
|
+
return url;
|
|
155
|
+
try {
|
|
156
|
+
const urlObj = new URL(url);
|
|
157
|
+
// Remove sensitive query parameters
|
|
158
|
+
const sensitiveParams = ['api_key', 'apikey', 'token', 'secret', 'auth'];
|
|
159
|
+
let hasSensitiveParams = false;
|
|
160
|
+
sensitiveParams.forEach(param => {
|
|
161
|
+
if (urlObj.searchParams.has(param)) {
|
|
162
|
+
urlObj.searchParams.set(param, '***REDACTED***');
|
|
163
|
+
hasSensitiveParams = true;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// If we modified parameters, add indicator
|
|
167
|
+
if (hasSensitiveParams) {
|
|
168
|
+
return urlObj.toString() + '#sanitized';
|
|
169
|
+
}
|
|
170
|
+
return url;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// If URL parsing fails, mask the entire URL
|
|
174
|
+
return '***REDACTED_URL***';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Validate and sanitize a DOI string
|
|
179
|
+
*/
|
|
180
|
+
export function sanitizeDoi(doi) {
|
|
181
|
+
if (!doi || typeof doi !== 'string') {
|
|
182
|
+
return { valid: false, sanitized: '', error: 'DOI must be a non-empty string' };
|
|
183
|
+
}
|
|
184
|
+
// Remove whitespace and common prefixes
|
|
185
|
+
let sanitized = doi.trim();
|
|
186
|
+
// Remove common DOI URL prefixes
|
|
187
|
+
const prefixes = [
|
|
188
|
+
'https://doi.org/',
|
|
189
|
+
'http://doi.org/',
|
|
190
|
+
'https://dx.doi.org/',
|
|
191
|
+
'http://dx.doi.org/',
|
|
192
|
+
'doi:',
|
|
193
|
+
'DOI:'
|
|
194
|
+
];
|
|
195
|
+
for (const prefix of prefixes) {
|
|
196
|
+
if (sanitized.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
197
|
+
sanitized = sanitized.substring(prefix.length);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Basic DOI format validation
|
|
202
|
+
// DOI should start with "10." followed by digits and then any characters
|
|
203
|
+
const doiPattern = /^10\.\d{4,}(\.\d+)*\/\S+$/;
|
|
204
|
+
if (!doiPattern.test(sanitized)) {
|
|
205
|
+
return { valid: false, sanitized: '', error: 'Invalid DOI format' };
|
|
206
|
+
}
|
|
207
|
+
// Additional safety checks
|
|
208
|
+
if (sanitized.length > 256) {
|
|
209
|
+
return { valid: false, sanitized: '', error: 'DOI too long (max 256 characters)' };
|
|
210
|
+
}
|
|
211
|
+
// Check for suspicious patterns
|
|
212
|
+
if (sanitized.includes('<') || sanitized.includes('>') ||
|
|
213
|
+
sanitized.includes('"') || sanitized.includes("'")) {
|
|
214
|
+
return { valid: false, sanitized: '', error: 'DOI contains invalid characters' };
|
|
215
|
+
}
|
|
216
|
+
return { valid: true, sanitized: sanitized };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Escape query value for different contexts
|
|
220
|
+
*/
|
|
221
|
+
export function escapeQueryValue(value, context = 'general') {
|
|
222
|
+
if (!value)
|
|
223
|
+
return '';
|
|
224
|
+
// Remove null bytes and control characters
|
|
225
|
+
let escaped = value.replace(/[\x00-\x1F\x7F]/g, '');
|
|
226
|
+
switch (context) {
|
|
227
|
+
case 'springer':
|
|
228
|
+
escaped = escaped
|
|
229
|
+
.replace(/"/g, '\\"') // Escape quotes
|
|
230
|
+
.replace(/[()]/g, '') // Remove parentheses
|
|
231
|
+
.replace(/;/g, '') // Remove semicolons
|
|
232
|
+
.replace(/\/\*/g, '') // Remove SQL comment start
|
|
233
|
+
.replace(/\*\//g, ''); // Remove SQL comment end
|
|
234
|
+
break;
|
|
235
|
+
case 'wos':
|
|
236
|
+
// For WoS, only remove quotes and parentheses if not user-provided field query
|
|
237
|
+
if (!escaped.includes('TS=') && !escaped.includes('TI=') &&
|
|
238
|
+
!escaped.includes('AU=') && !escaped.includes('SO=')) {
|
|
239
|
+
escaped = escaped
|
|
240
|
+
.replace(/"/g, '') // Remove quotes
|
|
241
|
+
.replace(/[()]/g, '') // Remove parentheses
|
|
242
|
+
.trim();
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
default:
|
|
246
|
+
escaped = escaped
|
|
247
|
+
.replace(/["<>]/g, '') // Remove quotes and angle brackets
|
|
248
|
+
.replace(/\/\/+/g, '') // Remove multiple slashes
|
|
249
|
+
.trim();
|
|
250
|
+
}
|
|
251
|
+
// Length limit to prevent DoS
|
|
252
|
+
if (escaped.length > 200) {
|
|
253
|
+
escaped = escaped.substring(0, 200);
|
|
254
|
+
}
|
|
255
|
+
return escaped.trim();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Validate query complexity to prevent DoS
|
|
259
|
+
*/
|
|
260
|
+
export function validateQueryComplexity(query, options = {}) {
|
|
261
|
+
const maxLength = options.maxLength || 1000;
|
|
262
|
+
const maxBooleanOperators = options.maxBooleanOperators || 10;
|
|
263
|
+
if (!query)
|
|
264
|
+
return { valid: true };
|
|
265
|
+
// Check length
|
|
266
|
+
if (query.length > maxLength) {
|
|
267
|
+
return {
|
|
268
|
+
valid: false,
|
|
269
|
+
error: `Query too long (max ${maxLength} characters)`
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
// Count boolean operators
|
|
273
|
+
const booleanOperators = query.match(/\b(AND|OR|NOT)\b/gi) || [];
|
|
274
|
+
if (booleanOperators.length > maxBooleanOperators) {
|
|
275
|
+
return {
|
|
276
|
+
valid: false,
|
|
277
|
+
error: `Query too complex (max ${maxBooleanOperators} boolean operators)`
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// Check for potential injection patterns
|
|
281
|
+
const injectionPatterns = [
|
|
282
|
+
/;\s*(drop|delete|update|insert|exec|union)/i,
|
|
283
|
+
/\/\*.*\*\//s, // SQL comments
|
|
284
|
+
/\/\/.*/, // Line comments
|
|
285
|
+
/\b(select|insert|update|delete|drop|create|alter|exec|execute|union)\b.*\b(from|where|and|or)\b/i,
|
|
286
|
+
/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/ // Control characters
|
|
287
|
+
];
|
|
288
|
+
for (const pattern of injectionPatterns) {
|
|
289
|
+
if (pattern.test(query)) {
|
|
290
|
+
return {
|
|
291
|
+
valid: false,
|
|
292
|
+
error: 'Query contains potentially dangerous patterns'
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return { valid: true };
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Create a timeout wrapper for promises
|
|
300
|
+
*/
|
|
301
|
+
export function withTimeout(promise, ms, message) {
|
|
302
|
+
let timer;
|
|
303
|
+
const timeout = new Promise((_, reject) => {
|
|
304
|
+
timer = setTimeout(() => {
|
|
305
|
+
reject(new Error(message || `Operation timed out after ${ms}ms`));
|
|
306
|
+
}, ms);
|
|
307
|
+
});
|
|
308
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Generate a correlation ID for request tracking
|
|
312
|
+
*/
|
|
313
|
+
export function generateCorrelationId() {
|
|
314
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Mask sensitive data in strings
|
|
318
|
+
*/
|
|
319
|
+
export function maskSensitiveData(str) {
|
|
320
|
+
if (!str || str.length < 8)
|
|
321
|
+
return '***';
|
|
322
|
+
const visibleChars = Math.min(4, Math.floor(str.length / 4));
|
|
323
|
+
return str.substring(0, visibleChars) +
|
|
324
|
+
'*'.repeat(str.length - visibleChars * 2) +
|
|
325
|
+
str.substring(str.length - visibleChars);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Check if a string looks like an API key or token
|
|
329
|
+
*/
|
|
330
|
+
export function looksLikeToken(str) {
|
|
331
|
+
if (!str || typeof str !== 'string')
|
|
332
|
+
return false;
|
|
333
|
+
// Common token patterns
|
|
334
|
+
const tokenPatterns = [
|
|
335
|
+
/^[a-zA-Z0-9_-]{20,}$/, // Long alphanumeric
|
|
336
|
+
/^Bearer\s+[a-zA-Z0-9_-]+$/, // Bearer token
|
|
337
|
+
/^Basic\s+[A-Za-z0-9+/=]+$/, // Basic auth
|
|
338
|
+
/^[0-9a-f]{32,}$/i, // Hex token
|
|
339
|
+
/^[A-Za-z0-9+/]{20,}={0,2}$/ // Base64-like
|
|
340
|
+
];
|
|
341
|
+
return tokenPatterns.some(pattern => pattern.test(str));
|
|
342
|
+
}
|
|
343
|
+
export default {
|
|
344
|
+
sanitizeRequest,
|
|
345
|
+
sanitizeHeaders,
|
|
346
|
+
sanitizeParams,
|
|
347
|
+
sanitizeBody,
|
|
348
|
+
sanitizeUrl,
|
|
349
|
+
sanitizeDoi,
|
|
350
|
+
escapeQueryValue,
|
|
351
|
+
validateQueryComplexity,
|
|
352
|
+
withTimeout,
|
|
353
|
+
generateCorrelationId,
|
|
354
|
+
maskSensitiveData,
|
|
355
|
+
looksLikeToken
|
|
356
|
+
};
|
|
357
|
+
//# sourceMappingURL=SecurityUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityUtils.js","sourceRoot":"","sources":["../../src/utils/SecurityUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "paper-search-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-friendly CLI for searching and downloading academic papers from multiple sources.",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"paper-search": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
12
|
+
"build": "npm run clean && tsc && node -e \"require('fs').chmodSync('dist/cli.js',0o755)\"",
|
|
13
|
+
"start": "node dist/cli.js",
|
|
14
|
+
"start:cli": "tsx src/cli.ts",
|
|
15
|
+
"dev": "tsx src/cli.ts",
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"lint": "eslint src/**/*.ts",
|
|
18
|
+
"format": "prettier --write src/**/*.ts",
|
|
19
|
+
"prepublishOnly": "npm test -- --runInBand && npm run build"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/",
|
|
23
|
+
"README.md",
|
|
24
|
+
"README-sc.md",
|
|
25
|
+
"skills/",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
".env.example"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"cli",
|
|
31
|
+
"academic-papers",
|
|
32
|
+
"research",
|
|
33
|
+
"arxiv",
|
|
34
|
+
"pubmed",
|
|
35
|
+
"web-of-science",
|
|
36
|
+
"nodejs",
|
|
37
|
+
"typescript"
|
|
38
|
+
],
|
|
39
|
+
"author": "dianel; CLI adaptation by dr-dumpling",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@types/cheerio": "^0.22.35",
|
|
43
|
+
"axios": "^1.7.2",
|
|
44
|
+
"cheerio": "^1.1.2",
|
|
45
|
+
"dotenv": "^16.4.5",
|
|
46
|
+
"lru-cache": "^11.2.5",
|
|
47
|
+
"pdf-parse": "^1.1.4",
|
|
48
|
+
"xml2js": "^0.6.2",
|
|
49
|
+
"zod": "^3.23.8"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/jest": "^29.5.14",
|
|
53
|
+
"@types/lru-cache": "^7.10.9",
|
|
54
|
+
"@types/node": "^20.14.10",
|
|
55
|
+
"@types/xml2js": "^0.4.14",
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
|
57
|
+
"@typescript-eslint/parser": "^7.16.0",
|
|
58
|
+
"eslint": "^8.57.0",
|
|
59
|
+
"jest": "^29.7.0",
|
|
60
|
+
"prettier": "^3.3.2",
|
|
61
|
+
"ts-jest": "^29.2.5",
|
|
62
|
+
"tsx": "^4.16.2",
|
|
63
|
+
"typescript": "^5.5.3"
|
|
64
|
+
},
|
|
65
|
+
"jest": {
|
|
66
|
+
"preset": "ts-jest/presets/default-esm",
|
|
67
|
+
"testEnvironment": "node",
|
|
68
|
+
"extensionsToTreatAsEsm": [
|
|
69
|
+
".ts"
|
|
70
|
+
],
|
|
71
|
+
"transformIgnorePatterns": [
|
|
72
|
+
"node_modules/(?!(p-limit|yocto-queue)/)"
|
|
73
|
+
],
|
|
74
|
+
"moduleNameMapper": {
|
|
75
|
+
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
76
|
+
},
|
|
77
|
+
"transform": {
|
|
78
|
+
"^.+\\.tsx?$": [
|
|
79
|
+
"ts-jest",
|
|
80
|
+
{
|
|
81
|
+
"useESM": true
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"testMatch": [
|
|
86
|
+
"**/tests/**/*.test.ts",
|
|
87
|
+
"**/__tests__/**/*.test.ts"
|
|
88
|
+
],
|
|
89
|
+
"setupFilesAfterEnv": [
|
|
90
|
+
"<rootDir>/tests/setup.ts"
|
|
91
|
+
],
|
|
92
|
+
"collectCoverageFrom": [
|
|
93
|
+
"src/**/*.ts",
|
|
94
|
+
"!src/**/*.d.ts"
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"overrides": {
|
|
98
|
+
"entities": "^6.0.0"
|
|
99
|
+
},
|
|
100
|
+
"engines": {
|
|
101
|
+
"node": ">=18.0.0"
|
|
102
|
+
},
|
|
103
|
+
"repository": {
|
|
104
|
+
"type": "git",
|
|
105
|
+
"url": "git+https://github.com/dr-dumpling/paper-search-cli.git"
|
|
106
|
+
},
|
|
107
|
+
"homepage": "https://github.com/dr-dumpling/paper-search-cli#readme",
|
|
108
|
+
"bugs": {
|
|
109
|
+
"url": "https://github.com/dr-dumpling/paper-search-cli/issues"
|
|
110
|
+
}
|
|
111
|
+
}
|