@danielszlaski/envguard 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/.envguardrc.example.json +17 -0
- package/LICENSE +21 -0
- package/README.md +320 -0
- package/dist/analyzer/envAnalyzer.d.ts +8 -0
- package/dist/analyzer/envAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/envAnalyzer.js +112 -0
- package/dist/analyzer/envAnalyzer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +9 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/fix.d.ts +4 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +115 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/scan.d.ts +9 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +274 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/config/configLoader.d.ts +35 -0
- package/dist/config/configLoader.d.ts.map +1 -0
- package/dist/config/configLoader.js +141 -0
- package/dist/config/configLoader.js.map +1 -0
- package/dist/constants/knownEnvVars.d.ts +38 -0
- package/dist/constants/knownEnvVars.d.ts.map +1 -0
- package/dist/constants/knownEnvVars.js +111 -0
- package/dist/constants/knownEnvVars.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/envParser.d.ts +13 -0
- package/dist/parser/envParser.d.ts.map +1 -0
- package/dist/parser/envParser.js +126 -0
- package/dist/parser/envParser.js.map +1 -0
- package/dist/parser/serverlessParser.d.ts +27 -0
- package/dist/parser/serverlessParser.d.ts.map +1 -0
- package/dist/parser/serverlessParser.js +162 -0
- package/dist/parser/serverlessParser.js.map +1 -0
- package/dist/scanner/codeScanner.d.ts +13 -0
- package/dist/scanner/codeScanner.d.ts.map +1 -0
- package/dist/scanner/codeScanner.js +157 -0
- package/dist/scanner/codeScanner.js.map +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
- package/src/analyzer/envAnalyzer.ts +133 -0
- package/src/cli.ts +54 -0
- package/src/commands/check.ts +6 -0
- package/src/commands/fix.ts +104 -0
- package/src/commands/scan.ts +289 -0
- package/src/config/configLoader.ts +131 -0
- package/src/constants/knownEnvVars.ts +108 -0
- package/src/index.ts +4 -0
- package/src/parser/envParser.ts +114 -0
- package/src/parser/serverlessParser.ts +146 -0
- package/src/scanner/codeScanner.ts +148 -0
- package/src/types.ts +26 -0
- package/test-project/.envguardrc.json +7 -0
- package/test-project/src/lambda1/.env.example +11 -0
- package/test-project/src/lambda1/handler.js +14 -0
- package/test-project/src/lambda2/.env.example +9 -0
- package/test-project/src/lambda2/handler.js +11 -0
- package/test-project/src/lambda2/handler2.js +13 -0
- package/test-project/src/lambda2/serverless.yml +50 -0
- package/test-project/src/payment/.env.example +23 -0
- package/test-project/src/payment/payment.js +14 -0
- package/test-project/src/payment/server.ts +11 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
|
|
4
|
+
export interface ServerlessEnvEntry {
|
|
5
|
+
key: string;
|
|
6
|
+
valueExpression: string; // e.g., "${ssm:/path}" or "hardcoded-value"
|
|
7
|
+
isReference: boolean; // true if it references SSM, secrets manager, etc.
|
|
8
|
+
source: string; // file path
|
|
9
|
+
lineNumber?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// CloudFormation intrinsic function types for js-yaml
|
|
13
|
+
const CF_SCHEMA = yaml.FAILSAFE_SCHEMA.extend([
|
|
14
|
+
new yaml.Type('!Ref', { kind: 'scalar', construct: (data) => ({ Ref: data }) }),
|
|
15
|
+
new yaml.Type('!GetAtt', { kind: 'scalar', construct: (data) => ({ 'Fn::GetAtt': data }) }),
|
|
16
|
+
new yaml.Type('!GetAtt', { kind: 'sequence', construct: (data) => ({ 'Fn::GetAtt': data }) }),
|
|
17
|
+
new yaml.Type('!Join', { kind: 'sequence', construct: (data) => ({ 'Fn::Join': data }) }),
|
|
18
|
+
new yaml.Type('!Sub', { kind: 'scalar', construct: (data) => ({ 'Fn::Sub': data }) }),
|
|
19
|
+
new yaml.Type('!Sub', { kind: 'sequence', construct: (data) => ({ 'Fn::Sub': data }) }),
|
|
20
|
+
new yaml.Type('!ImportValue', { kind: 'scalar', construct: (data) => ({ 'Fn::ImportValue': data }) }),
|
|
21
|
+
new yaml.Type('!Select', { kind: 'sequence', construct: (data) => ({ 'Fn::Select': data }) }),
|
|
22
|
+
new yaml.Type('!Split', { kind: 'sequence', construct: (data) => ({ 'Fn::Split': data }) }),
|
|
23
|
+
new yaml.Type('!FindInMap', { kind: 'sequence', construct: (data) => ({ 'Fn::FindInMap': data }) }),
|
|
24
|
+
new yaml.Type('!GetAZs', { kind: 'scalar', construct: (data) => ({ 'Fn::GetAZs': data }) }),
|
|
25
|
+
new yaml.Type('!Base64', { kind: 'scalar', construct: (data) => ({ 'Fn::Base64': data }) }),
|
|
26
|
+
new yaml.Type('!Equals', { kind: 'sequence', construct: (data) => ({ 'Fn::Equals': data }) }),
|
|
27
|
+
new yaml.Type('!Not', { kind: 'sequence', construct: (data) => ({ 'Fn::Not': data }) }),
|
|
28
|
+
new yaml.Type('!And', { kind: 'sequence', construct: (data) => ({ 'Fn::And': data }) }),
|
|
29
|
+
new yaml.Type('!Or', { kind: 'sequence', construct: (data) => ({ 'Fn::Or': data }) }),
|
|
30
|
+
new yaml.Type('!If', { kind: 'sequence', construct: (data) => ({ 'Fn::If': data }) }),
|
|
31
|
+
new yaml.Type('!Condition', { kind: 'scalar', construct: (data) => ({ Condition: data }) }),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export class ServerlessParser {
|
|
35
|
+
/**
|
|
36
|
+
* Parse a serverless.yml file and extract environment variables
|
|
37
|
+
* from the provider.environment section
|
|
38
|
+
*/
|
|
39
|
+
parse(filePath: string): Map<string, ServerlessEnvEntry> {
|
|
40
|
+
const envVars = new Map<string, ServerlessEnvEntry>();
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(filePath)) {
|
|
43
|
+
return envVars;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
48
|
+
const doc = yaml.load(content, { schema: CF_SCHEMA }) as any;
|
|
49
|
+
|
|
50
|
+
if (!doc || typeof doc !== 'object') {
|
|
51
|
+
return envVars;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Extract from provider.environment
|
|
55
|
+
const providerEnv = doc.provider?.environment;
|
|
56
|
+
if (providerEnv && typeof providerEnv === 'object') {
|
|
57
|
+
for (const [key, value] of Object.entries(providerEnv)) {
|
|
58
|
+
// Only include uppercase env var names (convention)
|
|
59
|
+
if (this.isValidEnvVarName(key)) {
|
|
60
|
+
const valueStr = String(value);
|
|
61
|
+
envVars.set(key, {
|
|
62
|
+
key,
|
|
63
|
+
valueExpression: valueStr,
|
|
64
|
+
isReference: this.isReference(valueStr),
|
|
65
|
+
source: filePath,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Also check for function-level environment variables
|
|
72
|
+
const functions = doc.functions;
|
|
73
|
+
if (functions && typeof functions === 'object') {
|
|
74
|
+
for (const [funcName, funcConfig] of Object.entries(functions)) {
|
|
75
|
+
const funcEnv = (funcConfig as any)?.environment;
|
|
76
|
+
if (funcEnv && typeof funcEnv === 'object') {
|
|
77
|
+
for (const [key, value] of Object.entries(funcEnv)) {
|
|
78
|
+
if (this.isValidEnvVarName(key)) {
|
|
79
|
+
const valueStr = String(value);
|
|
80
|
+
// Don't overwrite if already exists from provider level
|
|
81
|
+
if (!envVars.has(key)) {
|
|
82
|
+
envVars.set(key, {
|
|
83
|
+
key,
|
|
84
|
+
valueExpression: valueStr,
|
|
85
|
+
isReference: this.isReference(valueStr),
|
|
86
|
+
source: `${filePath} (function: ${funcName})`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`Error parsing ${filePath}:`, error);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return envVars;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if a value references external sources like SSM, Secrets Manager, etc.
|
|
103
|
+
*/
|
|
104
|
+
private isReference(value: string): boolean {
|
|
105
|
+
if (typeof value !== 'string') {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check for common serverless variable patterns
|
|
110
|
+
const referencePatterns = [
|
|
111
|
+
/\$\{ssm:/, // SSM Parameter Store
|
|
112
|
+
/\$\{aws:reference/, // AWS reference
|
|
113
|
+
/\$\{file\(/, // File reference
|
|
114
|
+
/\$\{self:custom\./, // Custom variables
|
|
115
|
+
/\$\{opt:/, // CLI options
|
|
116
|
+
/\$\{env:/, // Environment variables
|
|
117
|
+
/\$\{cf:/, // CloudFormation outputs
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
return referencePatterns.some(pattern => pattern.test(value));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validate if a key is a valid environment variable name
|
|
125
|
+
*/
|
|
126
|
+
private isValidEnvVarName(key: string): boolean {
|
|
127
|
+
// Environment variables should typically be uppercase with underscores
|
|
128
|
+
// But we'll be flexible and accept mixed case
|
|
129
|
+
return /^[A-Z_][A-Z0-9_]*$/i.test(key);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Find all serverless.yml files in a directory
|
|
134
|
+
*/
|
|
135
|
+
async findServerlessFiles(rootDir: string): Promise<string[]> {
|
|
136
|
+
const { glob } = await import('glob');
|
|
137
|
+
|
|
138
|
+
const files = await glob('**/serverless.{yml,yaml}', {
|
|
139
|
+
cwd: rootDir,
|
|
140
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**'],
|
|
141
|
+
absolute: true,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return files;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { ServerlessParser } from '../parser/serverlessParser';
|
|
5
|
+
|
|
6
|
+
export class CodeScanner {
|
|
7
|
+
private rootDir: string;
|
|
8
|
+
private excludePatterns: string[];
|
|
9
|
+
private serverlessParser: ServerlessParser;
|
|
10
|
+
|
|
11
|
+
constructor(rootDir: string, excludePatterns: string[] = ['node_modules', 'dist', 'build', '.git']) {
|
|
12
|
+
this.rootDir = rootDir;
|
|
13
|
+
this.excludePatterns = excludePatterns;
|
|
14
|
+
this.serverlessParser = new ServerlessParser();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async scan(): Promise<Map<string, string[]>> {
|
|
18
|
+
const envVars = new Map<string, string[]>();
|
|
19
|
+
|
|
20
|
+
// Scan for JavaScript/TypeScript files
|
|
21
|
+
const files = await glob('**/*.{js,ts,jsx,tsx,mjs,cjs}', {
|
|
22
|
+
cwd: this.rootDir,
|
|
23
|
+
ignore: this.excludePatterns.map(p => `**/${p}/**`),
|
|
24
|
+
absolute: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const vars = await this.scanFile(file);
|
|
29
|
+
for (const varName of vars) {
|
|
30
|
+
const relativePath = path.relative(this.rootDir, file);
|
|
31
|
+
if (!envVars.has(varName)) {
|
|
32
|
+
envVars.set(varName, []);
|
|
33
|
+
}
|
|
34
|
+
envVars.get(varName)!.push(relativePath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Also scan serverless.yml files for environment variable definitions
|
|
39
|
+
const serverlessFiles = await this.findServerlessFiles();
|
|
40
|
+
for (const file of serverlessFiles) {
|
|
41
|
+
const vars = this.scanServerlessFile(file);
|
|
42
|
+
for (const [varName, entry] of vars.entries()) {
|
|
43
|
+
const relativePath = path.relative(this.rootDir, file);
|
|
44
|
+
if (!envVars.has(varName)) {
|
|
45
|
+
envVars.set(varName, []);
|
|
46
|
+
}
|
|
47
|
+
envVars.get(varName)!.push(`${relativePath} (serverless config)`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return envVars;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async scanFile(filePath: string): Promise<Set<string>> {
|
|
55
|
+
const envVars = new Set<string>();
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
59
|
+
|
|
60
|
+
// Pattern 1: process.env.VAR_NAME
|
|
61
|
+
const processEnvPattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
62
|
+
let match;
|
|
63
|
+
|
|
64
|
+
while ((match = processEnvPattern.exec(content)) !== null) {
|
|
65
|
+
envVars.add(match[1]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Pattern 2: process.env['VAR_NAME'] or process.env["VAR_NAME"]
|
|
69
|
+
const processEnvBracketPattern = /process\.env\[['"]([A-Z_][A-Z0-9_]*)['"\]]/g;
|
|
70
|
+
|
|
71
|
+
while ((match = processEnvBracketPattern.exec(content)) !== null) {
|
|
72
|
+
envVars.add(match[1]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Pattern 3: Destructuring - const { VAR_NAME } = process.env
|
|
76
|
+
const destructuringPattern = /const\s+\{\s*([^}]+)\s*\}\s*=\s*process\.env/g;
|
|
77
|
+
|
|
78
|
+
while ((match = destructuringPattern.exec(content)) !== null) {
|
|
79
|
+
const vars = match[1].split(',').map(v => v.trim().split(':')[0].trim());
|
|
80
|
+
vars.forEach(v => {
|
|
81
|
+
if (/^[A-Z_][A-Z0-9_]*$/.test(v)) {
|
|
82
|
+
envVars.add(v);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`Error scanning file ${filePath}:`, error);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return envVars;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async findEnvFiles(): Promise<string[]> {
|
|
95
|
+
const envFiles = await glob('**/.env', {
|
|
96
|
+
cwd: this.rootDir,
|
|
97
|
+
ignore: this.excludePatterns.map(p => `**/${p}/**`),
|
|
98
|
+
absolute: true,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return envFiles;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async findServerlessFiles(): Promise<string[]> {
|
|
105
|
+
const serverlessFiles = await glob('**/serverless.{yml,yaml}', {
|
|
106
|
+
cwd: this.rootDir,
|
|
107
|
+
ignore: this.excludePatterns.map(p => `**/${p}/**`),
|
|
108
|
+
absolute: true,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return serverlessFiles;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
scanServerlessFile(filePath: string): Map<string, any> {
|
|
115
|
+
return this.serverlessParser.parse(filePath);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async scanByDirectory(): Promise<Map<string, Map<string, string[]>>> {
|
|
119
|
+
// Returns a map of directory -> (varName -> file locations)
|
|
120
|
+
const dirMap = new Map<string, Map<string, string[]>>();
|
|
121
|
+
|
|
122
|
+
// Scan for JavaScript/TypeScript files
|
|
123
|
+
const files = await glob('**/*.{js,ts,jsx,tsx,mjs,cjs}', {
|
|
124
|
+
cwd: this.rootDir,
|
|
125
|
+
ignore: this.excludePatterns.map(p => `**/${p}/**`),
|
|
126
|
+
absolute: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
const vars = await this.scanFile(file);
|
|
131
|
+
const fileDir = path.dirname(file);
|
|
132
|
+
const relativePath = path.relative(this.rootDir, file);
|
|
133
|
+
|
|
134
|
+
for (const varName of vars) {
|
|
135
|
+
if (!dirMap.has(fileDir)) {
|
|
136
|
+
dirMap.set(fileDir, new Map());
|
|
137
|
+
}
|
|
138
|
+
const varMap = dirMap.get(fileDir)!;
|
|
139
|
+
if (!varMap.has(varName)) {
|
|
140
|
+
varMap.set(varName, []);
|
|
141
|
+
}
|
|
142
|
+
varMap.get(varName)!.push(relativePath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return dirMap;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface EnvUsage {
|
|
2
|
+
varName: string;
|
|
3
|
+
locations: string[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface EnvDefinition {
|
|
7
|
+
varName: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
comment?: string;
|
|
10
|
+
source?: 'dotenv' | 'serverless' | 'both';
|
|
11
|
+
isReference?: boolean; // For serverless variables that reference external sources
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Issue {
|
|
15
|
+
type: 'missing' | 'unused' | 'undocumented';
|
|
16
|
+
varName: string;
|
|
17
|
+
details: string;
|
|
18
|
+
locations?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ScanResult {
|
|
22
|
+
issues: Issue[];
|
|
23
|
+
usedVars: Map<string, string[]>;
|
|
24
|
+
definedVars: Set<string>;
|
|
25
|
+
exampleVars: Set<string>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Auto-generated by envguard
|
|
2
|
+
# Do not put actual secrets in this file - use .env instead
|
|
3
|
+
|
|
4
|
+
# Used in: test-project/src/lambda1/handler.js
|
|
5
|
+
# Format: your-secret-here
|
|
6
|
+
CUSTOM_KEY=
|
|
7
|
+
|
|
8
|
+
# Used in: test-project/src/lambda1/handler.js
|
|
9
|
+
# Format: your-secret-here
|
|
10
|
+
STRIPE_SECRET_KEY=
|
|
11
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const stripe = require('stripe');
|
|
2
|
+
|
|
3
|
+
const { STRIPE_SECRET_KEY, CUSTOM_KEY } = process.env;
|
|
4
|
+
|
|
5
|
+
async function createPayment(amount) {
|
|
6
|
+
const stripeClient = stripe(STRIPE_SECRET_KEY);
|
|
7
|
+
|
|
8
|
+
return await stripeClient.paymentIntents.create({
|
|
9
|
+
amount,
|
|
10
|
+
currency: 'usd',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { createPayment };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
const OKTA_KEY = process.env.OKTA_DEV_CLIENT_ID;
|
|
3
|
+
|
|
4
|
+
const client = new SecretsManagerClient({ region: process.env.AWS_REGION });
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
module.exports.getPermissions = async (event) => {
|
|
8
|
+
return {
|
|
9
|
+
statusCode: 200,
|
|
10
|
+
body: JSON.stringify({ message: `Your OKTA_CLIENT_ID is ${OKTA_KEY}` }),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
service: ${self:app}-lambda2
|
|
2
|
+
app: my-app
|
|
3
|
+
|
|
4
|
+
custom:
|
|
5
|
+
cors-config:
|
|
6
|
+
origins:
|
|
7
|
+
- '*'
|
|
8
|
+
headers:
|
|
9
|
+
- Content-Type
|
|
10
|
+
- X-Amz-Date
|
|
11
|
+
- Authorization
|
|
12
|
+
- X-Api-Key
|
|
13
|
+
- X-Amz-Security-Token
|
|
14
|
+
- X-Amz-User-Agent
|
|
15
|
+
- token
|
|
16
|
+
- env
|
|
17
|
+
- provider
|
|
18
|
+
allowCredentials: false
|
|
19
|
+
|
|
20
|
+
secrets_INTERNAL_KEYS: ${ssm:/aws/reference/secretsmanager/${self:app}/${opt:stage, self:provider.stage}/internal-keys}
|
|
21
|
+
|
|
22
|
+
provider:
|
|
23
|
+
name: aws
|
|
24
|
+
runtime: nodejs20.x
|
|
25
|
+
memorySize: 8192
|
|
26
|
+
tags:
|
|
27
|
+
Product: ${opt:stage, self:provider.stage}-${self:app}
|
|
28
|
+
profile: abc
|
|
29
|
+
stage: dev
|
|
30
|
+
region: eu-central-1
|
|
31
|
+
deploymentBucket:
|
|
32
|
+
name: ${self:app}-deployment
|
|
33
|
+
versionFunctions: false
|
|
34
|
+
timeout: 29
|
|
35
|
+
environment:
|
|
36
|
+
NODE_ENV: ${file(./.env.yml):${opt:stage, self:provider.stage}.NODE_ENV}
|
|
37
|
+
STAGE: ${opt:stage, self:provider.stage}
|
|
38
|
+
OKTA_CLIENT_ID: ${self:custom.secrets_INTERNAL_KEYS.okta_client_id}
|
|
39
|
+
OKTA_API_PATH: ${self:custom.secrets_INTERNAL_KEYS.okta_api_path}
|
|
40
|
+
|
|
41
|
+
functions:
|
|
42
|
+
getPermissions:
|
|
43
|
+
handler: handler.getPermissions
|
|
44
|
+
description: 'Retrieves a list of all permissions available in the system.'
|
|
45
|
+
events:
|
|
46
|
+
- http:
|
|
47
|
+
path: /permissions
|
|
48
|
+
method: get
|
|
49
|
+
cors: ${self:custom.cors-config}
|
|
50
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Auto-generated by envguard
|
|
2
|
+
# Do not put actual secrets in this file - use .env instead
|
|
3
|
+
|
|
4
|
+
# Used in: test-project/src/payment/server.ts
|
|
5
|
+
# Format: your-api-key-here
|
|
6
|
+
API_KEY=
|
|
7
|
+
|
|
8
|
+
# Used in: test-project/src/payment/server.ts
|
|
9
|
+
# Format: postgresql://user:pass@host:5432/db
|
|
10
|
+
DATABASE_URL=
|
|
11
|
+
|
|
12
|
+
# Used in: test-project/src/payment/server.ts
|
|
13
|
+
# Format: 3000
|
|
14
|
+
PORT=
|
|
15
|
+
|
|
16
|
+
# Used in: test-project/src/payment/payment.js
|
|
17
|
+
# Format: your-secret-here
|
|
18
|
+
STRIPE_SECRET_KEY=
|
|
19
|
+
|
|
20
|
+
# Used in: test-project/src/payment/payment.js
|
|
21
|
+
# Format: your-secret-here
|
|
22
|
+
STRIPE_WEBHOOK_SECRET=
|
|
23
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const stripe = require('stripe');
|
|
2
|
+
|
|
3
|
+
const { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } = process.env;
|
|
4
|
+
|
|
5
|
+
async function createPayment(amount) {
|
|
6
|
+
const stripeClient = stripe(STRIPE_SECRET_KEY);
|
|
7
|
+
|
|
8
|
+
return await stripeClient.paymentIntents.create({
|
|
9
|
+
amount,
|
|
10
|
+
currency: 'usd',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { createPayment };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
const app = express();
|
|
4
|
+
const PORT = process.env.PORT || 3000;
|
|
5
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
6
|
+
const API_KEY = process.env.API_KEY;
|
|
7
|
+
|
|
8
|
+
app.listen(PORT, () => {
|
|
9
|
+
console.log(`Server running on port ${PORT}`);
|
|
10
|
+
console.log(`Database: ${DATABASE_URL}`);
|
|
11
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|