@@ -79,6 +79,19 @@ describe('Get Resources', () => {
7979 'Failed to execute the command to get resources.' ,
8080 ) ;
8181 } ) ;
82+
83+ test ( 'should throw when resource identifiers contain invalid characters' , async ( ) => {
84+ mockExecuteCommand . mockResolvedValue ( {
85+ exitCode : 0 ,
86+ stdOut : 'resource.aws_instance.example;rm -rf /' ,
87+ stdError : '' ,
88+ } ) ;
89+
90+ await expect ( getResources ( testTemplate ) ) . rejects . toThrow (
91+ 'Resource name contains invalid characters' ,
92+ ) ;
93+ expect ( mockExecuteCommand ) . toHaveBeenCalledTimes ( 1 ) ;
94+ } ) ;
8295} ) ;
8396
8497describe ( 'Update Resource Properties' , ( ) => {
@@ -102,6 +115,8 @@ describe('Update Resource Properties', () => {
102115 { propertyName : 'property_name1' , propertyValue : setPropertyValue } ,
103116 { propertyName : 'property_name2' , propertyValue : appendPropertyValue } ,
104117 ] ;
118+ const setValueArgument = `'${ setPropertyValueExpected } '` ;
119+ const appendValueArgument = `'${ appendPropertyValueExpected } '` ;
105120
106121 mockExecuteCommand
107122 . mockResolvedValueOnce ( {
@@ -146,7 +161,7 @@ describe('Update Resource Properties', () => {
146161 '/bin/bash' ,
147162 expect . arrayContaining ( [
148163 expect . stringContaining (
149- `attribute set resource.aws_instance.example.property_name1 ${ setPropertyValueExpected } | hcledit attribute append resource.aws_instance.example.property_name2 ${ appendPropertyValueExpected } ` ,
164+ `attribute set resource.aws_instance.example.property_name1 ${ setValueArgument } | hcledit attribute append resource.aws_instance.example.property_name2 ${ appendValueArgument } ` ,
150165 ) ,
151166 ] ) ,
152167 ) ;
@@ -175,6 +190,73 @@ describe('Update Resource Properties', () => {
175190 'Failed to modify the template. {"exitCode":1,"stdOut":"","stdError":"Error"}' ,
176191 ) ;
177192 } ) ;
193+
194+ test ( 'should escape property values to prevent shell injection' , async ( ) => {
195+ const modifications = [
196+ { propertyName : 'property_name1' , propertyValue : 'value; $(whoami)' } ,
197+ ] ;
198+
199+ mockExecuteCommand
200+ . mockResolvedValueOnce ( { exitCode : 1 , stdOut : '' , stdError : 'missing' } )
201+ . mockResolvedValueOnce ( { exitCode : 0 , stdOut : 'result' , stdError : '' } ) ;
202+
203+ const result = await updateResourceProperties (
204+ testTemplate ,
205+ 'aws_instance' ,
206+ 'example' ,
207+ modifications ,
208+ ) ;
209+
210+ expect ( result ) . toContain ( 'result' ) ;
211+ const executedCommand = mockExecuteCommand . mock . calls [ 1 ] [ 1 ] [ 1 ] ;
212+ expect ( executedCommand ) . toContain (
213+ `resource.aws_instance.example.property_name1 '\\"value; $(whoami)\\"'` ,
214+ ) ;
215+ } ) ;
216+
217+ test . each ( [
218+ {
219+ description : 'resource type contains invalid characters' ,
220+ resourceType : 'aws_instance; rm -rf /' ,
221+ resourceName : 'example' ,
222+ propertyName : 'property_name1' ,
223+ expectedMessage : 'Resource type contains invalid characters' ,
224+ } ,
225+ {
226+ description : 'resource name contains invalid characters' ,
227+ resourceType : 'aws_instance' ,
228+ resourceName : 'example; rm -rf /' ,
229+ propertyName : 'property_name1' ,
230+ expectedMessage : 'Resource name contains invalid characters' ,
231+ } ,
232+ {
233+ description : 'property name contains invalid characters' ,
234+ resourceType : 'aws_instance' ,
235+ resourceName : 'example' ,
236+ propertyName : 'property;name' ,
237+ expectedMessage : 'Property name at index 0 contains invalid characters' ,
238+ } ,
239+ ] ) (
240+ 'should throw when $description' ,
241+ async ( {
242+ description,
243+ resourceType,
244+ resourceName,
245+ propertyName,
246+ expectedMessage,
247+ } ) => {
248+ await expect (
249+ updateResourceProperties (
250+ testTemplate ,
251+ resourceType ,
252+ resourceName ,
253+ [ { propertyName, propertyValue : 'value' } ] ,
254+ ) ,
255+ ) . rejects . toThrow ( expectedMessage ) ;
256+ expect ( mockExecuteCommand ) . not . toHaveBeenCalled ( ) ;
257+ expect ( mockWriteFile ) . not . toHaveBeenCalled ( ) ;
258+ } ,
259+ ) ;
178260} ) ;
179261
180262describe ( 'Delete Resource' , ( ) => {
@@ -219,6 +301,14 @@ describe('Delete Resource', () => {
219301 'Failed to modify the template. {"exitCode":1,"stdOut":"","stdError":"Error"}' ,
220302 ) ;
221303 } ) ;
304+
305+ test ( 'should reject unsafe resource identifiers when deleting' , async ( ) => {
306+ await expect (
307+ deleteResource ( testTemplate , 'aws_instance' , 'example; rm -rf /' ) ,
308+ ) . rejects . toThrow ( 'Resource name contains invalid characters' ) ;
309+ expect ( mockExecuteCommand ) . not . toHaveBeenCalled ( ) ;
310+ expect ( mockWriteFile ) . not . toHaveBeenCalled ( ) ;
311+ } ) ;
222312} ) ;
223313
224314describe ( 'Update Variables File' , ( ) => {
@@ -266,6 +356,14 @@ describe('Update Variables File', () => {
266356 'Failed to modify the template. {"exitCode":1,"stdOut":"","stdError":"err"}' ,
267357 ) ;
268358 } ) ;
359+
360+ test ( 'should reject invalid variable names' , async ( ) => {
361+ await expect (
362+ updateVariablesFile ( 'a=1' , [ { variableName : 'name; rm -rf /' , variableValue : 3 } ] ) ,
363+ ) . rejects . toThrow ( 'Variable name at index 0 contains invalid characters' ) ;
364+ expect ( mockExecuteCommand ) . not . toHaveBeenCalled ( ) ;
365+ expect ( mockWriteFile ) . not . toHaveBeenCalled ( ) ;
366+ } ) ;
269367} ) ;
270368
271369describe ( 'List Blocks Command' , ( ) => {
0 commit comments