@friggframework/devtools 2.0.0--canary.623.b39b42d.0 → 2.0.0--canary.625.950ba82.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/frigg-cli/__tests__/unit/commands/generate-iam.test.js +97 -0
- package/frigg-cli/deploy-command/index.js +44 -0
- package/frigg-cli/generate-command/index.js +2 -1
- package/frigg-cli/generate-iam-command.js +2 -1
- package/frigg-cli/index.js +16 -1
- package/frigg-cli/ssm-command/index.js +270 -0
- package/frigg-cli/ssm-command/index.test.js +301 -0
- package/infrastructure/__tests__/helpers/test-utils.js +3 -5
- package/infrastructure/__tests__/scoped-environment.test.js +126 -0
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +17 -3
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +45 -0
- package/infrastructure/domains/database/migration-builder.js +46 -9
- package/infrastructure/domains/database/migration-builder.test.js +101 -0
- package/infrastructure/domains/integration/integration-builder.js +65 -9
- package/infrastructure/domains/integration/integration-builder.test.js +95 -0
- package/infrastructure/domains/networking/vpc-builder.js +57 -7
- package/infrastructure/domains/networking/vpc-builder.test.js +41 -0
- package/infrastructure/domains/networking/vpc-resolver.js +12 -3
- package/infrastructure/domains/networking/vpc-resolver.test.js +40 -0
- package/infrastructure/domains/parameters/offload-utils.js +175 -0
- package/infrastructure/domains/parameters/offload-utils.test.js +193 -0
- package/infrastructure/domains/parameters/ssm-builder.js +60 -14
- package/infrastructure/domains/parameters/ssm-builder.test.js +145 -1
- package/infrastructure/domains/scheduler/scheduler-builder.js +44 -8
- package/infrastructure/domains/scheduler/scheduler-builder.test.js +118 -0
- package/infrastructure/domains/security/iam-generator.js +33 -1
- package/infrastructure/domains/security/iam-generator.test.js +73 -0
- package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +15 -0
- package/infrastructure/domains/security/templates/iam-policy-full.json +7 -2
- package/infrastructure/domains/shared/builder-orchestrator.js +14 -0
- package/infrastructure/domains/shared/builder-orchestrator.test.js +45 -0
- package/infrastructure/domains/shared/environment-builder.js +43 -7
- package/infrastructure/domains/shared/environment-builder.test.js +97 -1
- package/infrastructure/domains/shared/function-environments.js +97 -0
- package/infrastructure/domains/shared/function-environments.test.js +146 -0
- package/infrastructure/infrastructure-composer.js +5 -0
- package/infrastructure/infrastructure-composer.test.js +69 -14
- package/infrastructure/integration.test.js +3 -5
- package/package.json +8 -7
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-function environment scoping (ADR-027)
|
|
3
|
+
*
|
|
4
|
+
* Builders emit `result.functionEnvironments` — a map of function name to
|
|
5
|
+
* env vars — instead of broadcasting framework vars app-wide through
|
|
6
|
+
* `result.environment`. The composer applies the merged map onto the final
|
|
7
|
+
* function definitions after every function (base + builder) exists.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether builders should scope framework env vars per function. Skipped in
|
|
12
|
+
* local mode: the serverless-plugin injects LocalStack queue URLs at
|
|
13
|
+
* provider level only, and function-level values would shadow them.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} appDefinition
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isScopedEnvironmentActive(appDefinition = {}) {
|
|
19
|
+
if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') return false;
|
|
20
|
+
return appDefinition.lambda?.scopedEnvironment === true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Apply a merged functionEnvironments map onto the composed functions.
|
|
25
|
+
* A key a builder already set directly on a function wins (e.g. the
|
|
26
|
+
* admin-script router's own SCHEDULER_ROLE_ARN must not be clobbered by
|
|
27
|
+
* the integration-scheduler value). An unknown function name is a hard
|
|
28
|
+
* error — silently dropping a var would surface as a runtime failure.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} functions - definition.functions (mutated)
|
|
31
|
+
* @param {Object} functionEnvironments - { fnName: { KEY: value } }
|
|
32
|
+
*/
|
|
33
|
+
function applyFunctionEnvironments(functions, functionEnvironments = {}) {
|
|
34
|
+
for (const [fnName, env] of Object.entries(functionEnvironments)) {
|
|
35
|
+
const fn = functions[fnName];
|
|
36
|
+
if (!fn) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`functionEnvironments targets unknown function '${fnName}' (known: ${Object.keys(
|
|
39
|
+
functions
|
|
40
|
+
).join(', ')})`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
fn.environment = { ...env, ...(fn.environment || {}) };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Function names the integration builder creates for one integration.
|
|
49
|
+
* Wire contract: must stay in sync with
|
|
50
|
+
* IntegrationBuilder.createFunctionDefinitions — a drift here surfaces as a
|
|
51
|
+
* hard unknown-function error at compose time, not a silent var drop.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} integration - entry from appDefinition.integrations
|
|
54
|
+
* @returns {string[]}
|
|
55
|
+
*/
|
|
56
|
+
function getIntegrationFunctionNames(integration) {
|
|
57
|
+
const name = integration.Definition.name;
|
|
58
|
+
const names = [name, `${name}QueueWorker`];
|
|
59
|
+
|
|
60
|
+
const webhooks = integration.Definition.webhooks;
|
|
61
|
+
if (webhooks === true || webhooks?.enabled === true) {
|
|
62
|
+
names.splice(1, 0, `${name}Webhook`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [bindingKey, binding] of Object.entries(
|
|
66
|
+
integration.Definition.extensions || {}
|
|
67
|
+
)) {
|
|
68
|
+
const routes = binding?.extension?.routes || [];
|
|
69
|
+
if (routes.length === 0) continue;
|
|
70
|
+
names.push(`${name}__${String(bindingKey).replace(/[^A-Za-z0-9]/g, '')}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return names;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Admin-script functions, when the feature is on (mirrors
|
|
78
|
+
* AdminScriptBuilder.shouldExecute). Both can instantiate arbitrary
|
|
79
|
+
* integrations, so they belong in every integration's queue-URL consumer
|
|
80
|
+
* set.
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} appDefinition
|
|
83
|
+
* @returns {string[]}
|
|
84
|
+
*/
|
|
85
|
+
function getAdminFunctionNames(appDefinition = {}) {
|
|
86
|
+
return Array.isArray(appDefinition.adminScripts) &&
|
|
87
|
+
appDefinition.adminScripts.length > 0
|
|
88
|
+
? ['adminScriptRouter', 'adminScriptExecutor']
|
|
89
|
+
: [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
isScopedEnvironmentActive,
|
|
94
|
+
applyFunctionEnvironments,
|
|
95
|
+
getIntegrationFunctionNames,
|
|
96
|
+
getAdminFunctionNames,
|
|
97
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const {
|
|
2
|
+
isScopedEnvironmentActive,
|
|
3
|
+
applyFunctionEnvironments,
|
|
4
|
+
} = require('./function-environments');
|
|
5
|
+
|
|
6
|
+
describe('function-environments', () => {
|
|
7
|
+
const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
if (originalSkipDiscovery === undefined) {
|
|
11
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
12
|
+
} else {
|
|
13
|
+
process.env.FRIGG_SKIP_AWS_DISCOVERY = originalSkipDiscovery;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('isScopedEnvironmentActive', () => {
|
|
18
|
+
it('is true only when lambda.scopedEnvironment is set', () => {
|
|
19
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
20
|
+
expect(
|
|
21
|
+
isScopedEnvironmentActive({
|
|
22
|
+
lambda: { scopedEnvironment: true },
|
|
23
|
+
})
|
|
24
|
+
).toBe(true);
|
|
25
|
+
expect(isScopedEnvironmentActive({})).toBe(false);
|
|
26
|
+
expect(
|
|
27
|
+
isScopedEnvironmentActive({
|
|
28
|
+
lambda: { scopedEnvironment: false },
|
|
29
|
+
})
|
|
30
|
+
).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('is false in local mode: the serverless-plugin injects LocalStack queue URLs at provider level only', () => {
|
|
34
|
+
process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
|
|
35
|
+
expect(
|
|
36
|
+
isScopedEnvironmentActive({
|
|
37
|
+
lambda: { scopedEnvironment: true },
|
|
38
|
+
})
|
|
39
|
+
).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getIntegrationFunctionNames', () => {
|
|
44
|
+
const { getIntegrationFunctionNames } = require('./function-environments');
|
|
45
|
+
|
|
46
|
+
it('always includes the router and queue worker', () => {
|
|
47
|
+
expect(
|
|
48
|
+
getIntegrationFunctionNames({ Definition: { name: 'hubspot' } })
|
|
49
|
+
).toEqual(['hubspot', 'hubspotQueueWorker']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('includes the webhook handler when webhooks are enabled', () => {
|
|
53
|
+
expect(
|
|
54
|
+
getIntegrationFunctionNames({
|
|
55
|
+
Definition: { name: 'hubspot', webhooks: true },
|
|
56
|
+
})
|
|
57
|
+
).toContain('hubspotWebhook');
|
|
58
|
+
expect(
|
|
59
|
+
getIntegrationFunctionNames({
|
|
60
|
+
Definition: { name: 'hubspot', webhooks: { enabled: true } },
|
|
61
|
+
})
|
|
62
|
+
).toContain('hubspotWebhook');
|
|
63
|
+
expect(
|
|
64
|
+
getIntegrationFunctionNames({
|
|
65
|
+
Definition: { name: 'hubspot', webhooks: { enabled: false } },
|
|
66
|
+
})
|
|
67
|
+
).not.toContain('hubspotWebhook');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('includes one function per routed extension binding, sanitized', () => {
|
|
71
|
+
const names = getIntegrationFunctionNames({
|
|
72
|
+
Definition: {
|
|
73
|
+
name: 'hubspot',
|
|
74
|
+
extensions: {
|
|
75
|
+
'my-ext': { extension: { routes: [{ path: '/x', method: 'GET' }] } },
|
|
76
|
+
routeless: { extension: { routes: [] } },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
expect(names).toContain('hubspot__myext');
|
|
81
|
+
expect(names).not.toContain('hubspot__routeless');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('getAdminFunctionNames', () => {
|
|
86
|
+
const { getAdminFunctionNames } = require('./function-environments');
|
|
87
|
+
|
|
88
|
+
it('returns the admin functions only when admin scripts are configured', () => {
|
|
89
|
+
expect(
|
|
90
|
+
getAdminFunctionNames({ adminScripts: [{ Definition: { name: 'x' } }] })
|
|
91
|
+
).toEqual(['adminScriptRouter', 'adminScriptExecutor']);
|
|
92
|
+
expect(getAdminFunctionNames({})).toEqual([]);
|
|
93
|
+
expect(getAdminFunctionNames({ adminScripts: [] })).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('applyFunctionEnvironments', () => {
|
|
98
|
+
const makeFunctions = () => ({
|
|
99
|
+
auth: { handler: 'auth.handler' },
|
|
100
|
+
hubspot: {
|
|
101
|
+
handler: 'hubspot.handler',
|
|
102
|
+
environment: { EXISTING: 'builder-set' },
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('assigns scoped env vars onto the target functions', () => {
|
|
107
|
+
const functions = makeFunctions();
|
|
108
|
+
applyFunctionEnvironments(functions, {
|
|
109
|
+
auth: { HUBSPOT_QUEUE_URL: 'url-1' },
|
|
110
|
+
hubspot: { HUBSPOT_QUEUE_URL: 'url-1' },
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(functions.auth.environment).toEqual({
|
|
114
|
+
HUBSPOT_QUEUE_URL: 'url-1',
|
|
115
|
+
});
|
|
116
|
+
expect(functions.hubspot.environment).toEqual({
|
|
117
|
+
EXISTING: 'builder-set',
|
|
118
|
+
HUBSPOT_QUEUE_URL: 'url-1',
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('never clobbers a key a builder already set directly on the function', () => {
|
|
123
|
+
const functions = makeFunctions();
|
|
124
|
+
applyFunctionEnvironments(functions, {
|
|
125
|
+
hubspot: { EXISTING: 'scoped-value', OTHER: 'x' },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(functions.hubspot.environment.EXISTING).toBe('builder-set');
|
|
129
|
+
expect(functions.hubspot.environment.OTHER).toBe('x');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('throws on an unknown function name instead of silently dropping vars', () => {
|
|
133
|
+
expect(() =>
|
|
134
|
+
applyFunctionEnvironments(makeFunctions(), {
|
|
135
|
+
typoFunction: { A: '1' },
|
|
136
|
+
})
|
|
137
|
+
).toThrow(/typoFunction/);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('is a no-op for an empty map', () => {
|
|
141
|
+
const functions = makeFunctions();
|
|
142
|
+
applyFunctionEnvironments(functions, {});
|
|
143
|
+
expect(functions.auth.environment).toBeUndefined();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -20,6 +20,7 @@ const { SchedulerBuilder } = require('./domains/scheduler/scheduler-builder');
|
|
|
20
20
|
const { AdminScriptBuilder } = require('./domains/admin-scripts/admin-script-builder');
|
|
21
21
|
|
|
22
22
|
// Utilities
|
|
23
|
+
const { applyFunctionEnvironments } = require('./domains/shared/function-environments');
|
|
23
24
|
const { modifyHandlerPaths } = require('./domains/shared/utilities/handler-path-resolver');
|
|
24
25
|
const { createBaseDefinition } = require('./domains/shared/utilities/base-definition-factory');
|
|
25
26
|
const { ensurePrismaLayerExists } = require('./domains/shared/utilities/prisma-layer-manager');
|
|
@@ -75,6 +76,10 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
75
76
|
definition.provider.iamRoleStatements.push(...merged.iamStatements);
|
|
76
77
|
Object.assign(definition.provider.environment, merged.environment);
|
|
77
78
|
Object.assign(definition.functions, merged.functions);
|
|
79
|
+
applyFunctionEnvironments(
|
|
80
|
+
definition.functions,
|
|
81
|
+
merged.functionEnvironments
|
|
82
|
+
);
|
|
78
83
|
|
|
79
84
|
if (merged.vpcConfig) {
|
|
80
85
|
definition.provider.vpc = merged.vpcConfig;
|
|
@@ -1062,7 +1062,18 @@ describe('composeServerlessDefinition', () => {
|
|
|
1062
1062
|
});
|
|
1063
1063
|
|
|
1064
1064
|
describe('SSM Configuration', () => {
|
|
1065
|
-
|
|
1065
|
+
const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
1066
|
+
|
|
1067
|
+
afterEach(() => {
|
|
1068
|
+
if (originalSkipDiscovery === undefined) {
|
|
1069
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
1070
|
+
} else {
|
|
1071
|
+
process.env.FRIGG_SKIP_AWS_DISCOVERY = originalSkipDiscovery;
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
it('should add the broad SSM read grant when ssm.enable is true without offload', async () => {
|
|
1076
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
1066
1077
|
const appDefinition = {
|
|
1067
1078
|
ssm: { enable: true },
|
|
1068
1079
|
integrations: []
|
|
@@ -1070,12 +1081,10 @@ describe('composeServerlessDefinition', () => {
|
|
|
1070
1081
|
|
|
1071
1082
|
const result = await composeServerlessDefinition(appDefinition);
|
|
1072
1083
|
|
|
1073
|
-
//
|
|
1074
|
-
expect(result.provider.layers).
|
|
1075
|
-
'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
|
|
1076
|
-
]);
|
|
1084
|
+
// We deliberately do NOT use the AWS Parameters-and-Secrets extension layer
|
|
1085
|
+
expect(result.provider.layers).toBeUndefined();
|
|
1077
1086
|
|
|
1078
|
-
//
|
|
1087
|
+
// Broad read grant present
|
|
1079
1088
|
const ssmPermission = result.provider.iamRoleStatements.find(
|
|
1080
1089
|
statement => statement.Action.includes('ssm:GetParameter')
|
|
1081
1090
|
);
|
|
@@ -1086,13 +1095,56 @@ describe('composeServerlessDefinition', () => {
|
|
|
1086
1095
|
'ssm:GetParameters',
|
|
1087
1096
|
'ssm:GetParametersByPath'
|
|
1088
1097
|
],
|
|
1089
|
-
Resource:
|
|
1090
|
-
'arn:aws:ssm:${
|
|
1091
|
-
|
|
1098
|
+
Resource: {
|
|
1099
|
+
'Fn::Sub': 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*'
|
|
1100
|
+
}
|
|
1092
1101
|
});
|
|
1093
1102
|
|
|
1094
|
-
//
|
|
1095
|
-
expect(result.provider.environment.SSM_PARAMETER_PREFIX).
|
|
1103
|
+
// No offload markers -> no offload env vars
|
|
1104
|
+
expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
|
|
1105
|
+
expect(result.provider.environment.FRIGG_SSM_OFFLOADED_KEYS).toBeUndefined();
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
it('should offload marked keys and expose prefix + offloaded keys env vars', async () => {
|
|
1109
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
1110
|
+
const appDefinition = {
|
|
1111
|
+
ssm: { enable: true },
|
|
1112
|
+
environment: { FOO: 'ssm', BAR: true },
|
|
1113
|
+
integrations: []
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
const result = await composeServerlessDefinition(appDefinition);
|
|
1117
|
+
|
|
1118
|
+
// BAR stays in the Lambda env, FOO is offloaded out of it
|
|
1119
|
+
expect(result.provider.environment.BAR).toBe("${env:BAR, ''}");
|
|
1120
|
+
expect(result.provider.environment.FOO).toBeUndefined();
|
|
1121
|
+
|
|
1122
|
+
// Offload env contract
|
|
1123
|
+
expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBe(
|
|
1124
|
+
'/frigg/${self:service}/${self:provider.stage}'
|
|
1125
|
+
);
|
|
1126
|
+
expect(result.provider.environment.FRIGG_SSM_OFFLOADED_KEYS).toBe('FOO');
|
|
1127
|
+
|
|
1128
|
+
// Prefix-scoped read grant present alongside the broad grant
|
|
1129
|
+
const scoped = result.provider.iamRoleStatements.find(
|
|
1130
|
+
statement => statement.Resource === 'arn:aws:ssm:${self:provider.region}:${aws:accountId}:parameter/frigg/${self:service}/${self:provider.stage}/*'
|
|
1131
|
+
);
|
|
1132
|
+
expect(scoped).toBeDefined();
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
it('should not add offload env vars when ssm.enable is true but nothing is marked', async () => {
|
|
1136
|
+
delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
1137
|
+
const appDefinition = {
|
|
1138
|
+
ssm: { enable: true },
|
|
1139
|
+
environment: { BAR: true },
|
|
1140
|
+
integrations: []
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
const result = await composeServerlessDefinition(appDefinition);
|
|
1144
|
+
|
|
1145
|
+
expect(result.provider.environment.BAR).toBe("${env:BAR, ''}");
|
|
1146
|
+
expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
|
|
1147
|
+
expect(result.provider.environment.FRIGG_SSM_OFFLOADED_KEYS).toBeUndefined();
|
|
1096
1148
|
});
|
|
1097
1149
|
|
|
1098
1150
|
it('should not add SSM configuration when ssm.enable is false', async () => {
|
|
@@ -1397,9 +1449,12 @@ describe('composeServerlessDefinition', () => {
|
|
|
1397
1449
|
expect(result.provider.environment.KMS_KEY_ARN).toBeDefined();
|
|
1398
1450
|
expect(result.custom.kmsGrants).toBeDefined();
|
|
1399
1451
|
|
|
1400
|
-
// SSM
|
|
1401
|
-
|
|
1402
|
-
|
|
1452
|
+
// SSM (enabled without offload markers -> broad grant only, no offload env vars)
|
|
1453
|
+
const ssmPermission = result.provider.iamRoleStatements.find(
|
|
1454
|
+
statement => statement.Action && statement.Action.includes('ssm:GetParameter')
|
|
1455
|
+
);
|
|
1456
|
+
expect(ssmPermission).toBeDefined();
|
|
1457
|
+
expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
|
|
1403
1458
|
|
|
1404
1459
|
// Integration
|
|
1405
1460
|
expect(result.functions.testIntegration).toBeDefined();
|
|
@@ -108,11 +108,9 @@ describe('VPC/KMS/SSM Integration Tests', () => {
|
|
|
108
108
|
);
|
|
109
109
|
expect(kmsPermission).toBeDefined();
|
|
110
110
|
|
|
111
|
-
// Verify SSM configuration
|
|
112
|
-
expect(serverlessConfig.provider.layers).
|
|
113
|
-
|
|
114
|
-
]);
|
|
115
|
-
expect(serverlessConfig.provider.environment.SSM_PARAMETER_PREFIX).toBe('/${self:service}/${self:provider.stage}');
|
|
111
|
+
// Verify SSM configuration (enabled without offload markers -> broad grant only)
|
|
112
|
+
expect(serverlessConfig.provider.layers).toBeUndefined();
|
|
113
|
+
expect(serverlessConfig.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
|
|
116
114
|
|
|
117
115
|
// Verify SSM IAM permissions
|
|
118
116
|
const ssmPermission = serverlessConfig.provider.iamRoleStatements.find(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.625.950ba82.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -21,13 +21,14 @@
|
|
|
21
21
|
"@aws-sdk/client-rds": "^3.906.0",
|
|
22
22
|
"@aws-sdk/client-s3": "^3.917.0",
|
|
23
23
|
"@aws-sdk/client-secrets-manager": "^3.906.0",
|
|
24
|
+
"@aws-sdk/client-ssm": "^3.906.0",
|
|
24
25
|
"@aws-sdk/client-sts": "^3.835.0",
|
|
25
26
|
"@babel/eslint-parser": "^7.18.9",
|
|
26
27
|
"@babel/parser": "^7.25.3",
|
|
27
28
|
"@babel/traverse": "^7.25.3",
|
|
28
|
-
"@friggframework/core": "2.0.0--canary.
|
|
29
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
30
|
-
"@friggframework/test": "2.0.0--canary.
|
|
29
|
+
"@friggframework/core": "2.0.0--canary.625.950ba82.0",
|
|
30
|
+
"@friggframework/schemas": "2.0.0--canary.625.950ba82.0",
|
|
31
|
+
"@friggframework/test": "2.0.0--canary.625.950ba82.0",
|
|
31
32
|
"@hapi/boom": "^10.0.1",
|
|
32
33
|
"@inquirer/prompts": "^5.3.8",
|
|
33
34
|
"axios": "^1.18.0",
|
|
@@ -55,8 +56,8 @@
|
|
|
55
56
|
"validate-npm-package-name": "^5.0.0"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
59
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
59
|
+
"@friggframework/eslint-config": "2.0.0--canary.625.950ba82.0",
|
|
60
|
+
"@friggframework/prettier-config": "2.0.0--canary.625.950ba82.0",
|
|
60
61
|
"aws-sdk-client-mock": "^4.1.0",
|
|
61
62
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
62
63
|
"jest": "^30.1.3",
|
|
@@ -88,5 +89,5 @@
|
|
|
88
89
|
"publishConfig": {
|
|
89
90
|
"access": "public"
|
|
90
91
|
},
|
|
91
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "950ba828b2885a01768dbfbee5a1fd531efb89ab"
|
|
92
93
|
}
|