cfn-check 0.3.1__tar.gz → 0.3.3__tar.gz

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.

Potentially problematic release.


This version of cfn-check might be problematic. Click here for more details.

Files changed (40) hide show
  1. {cfn_check-0.3.1 → cfn_check-0.3.3}/PKG-INFO +52 -6
  2. {cfn_check-0.3.1 → cfn_check-0.3.3}/README.md +52 -6
  3. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/evaluator.py +1 -1
  4. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/parsing/token.py +26 -8
  5. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/PKG-INFO +52 -6
  6. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/SOURCES.txt +1 -0
  7. cfn_check-0.3.3/example/pydantic_rules.py +15 -0
  8. {cfn_check-0.3.1 → cfn_check-0.3.3}/example/rules.py +11 -2
  9. {cfn_check-0.3.1 → cfn_check-0.3.3}/pyproject.toml +1 -1
  10. {cfn_check-0.3.1 → cfn_check-0.3.3}/LICENSE +0 -0
  11. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/__init__.py +0 -0
  12. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/__init__.py +0 -0
  13. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/root.py +0 -0
  14. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/utils/__init__.py +0 -0
  15. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/utils/attributes.py +0 -0
  16. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/utils/files.py +0 -0
  17. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/cli/validate.py +0 -0
  18. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/collection/__init__.py +0 -0
  19. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/collection/collection.py +0 -0
  20. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/__init__.py +0 -0
  21. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/errors.py +0 -0
  22. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/parsing/__init__.py +0 -0
  23. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/parsing/query_parser.py +0 -0
  24. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/parsing/token_type.py +0 -0
  25. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/evaluation/validate.py +0 -0
  26. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/loader/__init__.py +0 -0
  27. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/loader/loader.py +0 -0
  28. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/logging/__init__.py +0 -0
  29. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/logging/models.py +0 -0
  30. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/rules/__init__.py +0 -0
  31. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/rules/rule.py +0 -0
  32. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/shared/__init__.py +0 -0
  33. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/shared/types.py +0 -0
  34. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/validation/__init__.py +0 -0
  35. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check/validation/validator.py +0 -0
  36. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/dependency_links.txt +0 -0
  37. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/entry_points.txt +0 -0
  38. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/requires.txt +0 -0
  39. {cfn_check-0.3.1 → cfn_check-0.3.3}/cfn_check.egg-info/top_level.txt +0 -0
  40. {cfn_check-0.3.1 → cfn_check-0.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfn-check
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Validate Cloud Formation
5
5
  Author-email: Ada Lundhe <adalundhe@lundhe.audio>
6
6
  License: MIT License
@@ -50,7 +50,7 @@ Dynamic: license-file
50
50
 
51
51
  | Package | cfn-check |
52
52
  | ----------- | ----------- |
53
- | Version | 0.2.0 |
53
+ | Version | 0.3.3 |
54
54
  | Download | https://pypi.org/project/cfn-check/ |
55
55
  | Source | https://github.com/adalundhe/cfn-check |
56
56
  | Keywords | cloud-formation, testing, aws, cli |
@@ -314,7 +314,7 @@ class ValidateSecurityGroups(Collection):
314
314
  "Resources::SecurityGroup::Properties::(SecurityGroup)",
315
315
  "It checks Security Groups are correctly definined",
316
316
  )
317
- def validate_test(self, value: list[dict]):
317
+ def validate_security_groups(self, value: list[dict]):
318
318
  assert len(value) > 0
319
319
 
320
320
  for item in value:
@@ -352,7 +352,7 @@ To select all `Resource` objects, then further extract the `Type` field from eac
352
352
  Ranges allow you to perform sophisticated selection of objects or data within a CloudFormation document.
353
353
 
354
354
  > [!IMPORTANT]
355
- > Range Tokens *only* work on list items. This means that any
355
+ > Range Tokens *only* work on arrays. This means that any
356
356
  > values or other objects/data in the selected section of the
357
357
  > CloudFormation document will be *ignored* and filtered out.
358
358
 
@@ -456,7 +456,7 @@ The Rule will fail as below:
456
456
  error: ❌ No results matching results for query Resources::[*]::Type
457
457
  ```
458
458
 
459
- As we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
459
+ as we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
460
460
 
461
461
  ```yaml
462
462
  AppendItemToListFunction:
@@ -514,7 +514,7 @@ will select any EC2 ImageIds that start with either `AWSRegion` or `Custom`.
514
514
 
515
515
  ### Nested Ranges
516
516
 
517
- CloudFormation often involes nested arrays, and navigates these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
517
+ CloudFormation often involes nested arrays, and navigating these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
518
518
 
519
519
  ```yaml
520
520
  ZipFile: !Join
@@ -537,5 +537,51 @@ With Nested Ranges, this can be shortened to:
537
537
 
538
538
  ```
539
539
  Resources::AppendItemToListFunction::Properties::Code::ZipFile::[[]]
540
+ ```
540
541
 
541
542
  Which is both more concise *and* more representitave of our intention to select only the array.
543
+
544
+ <br/>
545
+
546
+ # Using Pydantic Models
547
+
548
+ In addition to traditional `pytest`-like assert statements, `cfn-lint` can validate results returned by queries via `Pydantic` models.
549
+
550
+ For example, consider again the initial example where we validate the `Type` field of `Resource` objects.
551
+
552
+ ```python
553
+ from cfn_check import Collection, Rule
554
+
555
+
556
+ class ValidateResourceType(Collection):
557
+
558
+ @Rule(
559
+ "Resources::*::Type",
560
+ "It checks Resource::Type is correctly definined",
561
+ )
562
+ def validate_test(self, value: str):
563
+ assert value is not None, '❌ Resource Type not defined'
564
+ assert isinstance(value, str), '❌ Resource Type not a string'
565
+ ```
566
+
567
+ Rather than explicitly querying for the type field and writing assertions, we can instead define a `Pydantic` schema, then pass all `Resource` objects to that schema by specifying it as a Python type hint in our `Rule` method's signature.
568
+
569
+ ```python
570
+ from cfn_check import Collection, Rule
571
+ from pydantic import BaseModel, StrictStr
572
+
573
+ class Resource(BaseModel):
574
+ Type: StrictStr
575
+
576
+
577
+ class ValidateResourceType(Collection):
578
+
579
+ @Rule(
580
+ "Resources::*",
581
+ "It checks Resource::Type is correctly definined",
582
+ )
583
+ def validate_test(self, value: Resource):
584
+ assert value is not None
585
+ ```
586
+
587
+ By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
@@ -9,7 +9,7 @@
9
9
 
10
10
  | Package | cfn-check |
11
11
  | ----------- | ----------- |
12
- | Version | 0.2.0 |
12
+ | Version | 0.3.3 |
13
13
  | Download | https://pypi.org/project/cfn-check/ |
14
14
  | Source | https://github.com/adalundhe/cfn-check |
15
15
  | Keywords | cloud-formation, testing, aws, cli |
@@ -273,7 +273,7 @@ class ValidateSecurityGroups(Collection):
273
273
  "Resources::SecurityGroup::Properties::(SecurityGroup)",
274
274
  "It checks Security Groups are correctly definined",
275
275
  )
276
- def validate_test(self, value: list[dict]):
276
+ def validate_security_groups(self, value: list[dict]):
277
277
  assert len(value) > 0
278
278
 
279
279
  for item in value:
@@ -311,7 +311,7 @@ To select all `Resource` objects, then further extract the `Type` field from eac
311
311
  Ranges allow you to perform sophisticated selection of objects or data within a CloudFormation document.
312
312
 
313
313
  > [!IMPORTANT]
314
- > Range Tokens *only* work on list items. This means that any
314
+ > Range Tokens *only* work on arrays. This means that any
315
315
  > values or other objects/data in the selected section of the
316
316
  > CloudFormation document will be *ignored* and filtered out.
317
317
 
@@ -415,7 +415,7 @@ The Rule will fail as below:
415
415
  error: ❌ No results matching results for query Resources::[*]::Type
416
416
  ```
417
417
 
418
- As we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
418
+ as we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
419
419
 
420
420
  ```yaml
421
421
  AppendItemToListFunction:
@@ -473,7 +473,7 @@ will select any EC2 ImageIds that start with either `AWSRegion` or `Custom`.
473
473
 
474
474
  ### Nested Ranges
475
475
 
476
- CloudFormation often involes nested arrays, and navigates these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
476
+ CloudFormation often involes nested arrays, and navigating these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
477
477
 
478
478
  ```yaml
479
479
  ZipFile: !Join
@@ -496,5 +496,51 @@ With Nested Ranges, this can be shortened to:
496
496
 
497
497
  ```
498
498
  Resources::AppendItemToListFunction::Properties::Code::ZipFile::[[]]
499
+ ```
500
+
501
+ Which is both more concise *and* more representitave of our intention to select only the array.
502
+
503
+ <br/>
504
+
505
+ # Using Pydantic Models
506
+
507
+ In addition to traditional `pytest`-like assert statements, `cfn-lint` can validate results returned by queries via `Pydantic` models.
508
+
509
+ For example, consider again the initial example where we validate the `Type` field of `Resource` objects.
510
+
511
+ ```python
512
+ from cfn_check import Collection, Rule
513
+
514
+
515
+ class ValidateResourceType(Collection):
516
+
517
+ @Rule(
518
+ "Resources::*::Type",
519
+ "It checks Resource::Type is correctly definined",
520
+ )
521
+ def validate_test(self, value: str):
522
+ assert value is not None, '❌ Resource Type not defined'
523
+ assert isinstance(value, str), '❌ Resource Type not a string'
524
+ ```
525
+
526
+ Rather than explicitly querying for the type field and writing assertions, we can instead define a `Pydantic` schema, then pass all `Resource` objects to that schema by specifying it as a Python type hint in our `Rule` method's signature.
527
+
528
+ ```python
529
+ from cfn_check import Collection, Rule
530
+ from pydantic import BaseModel, StrictStr
531
+
532
+ class Resource(BaseModel):
533
+ Type: StrictStr
534
+
535
+
536
+ class ValidateResourceType(Collection):
537
+
538
+ @Rule(
539
+ "Resources::*",
540
+ "It checks Resource::Type is correctly definined",
541
+ )
542
+ def validate_test(self, value: Resource):
543
+ assert value is not None
544
+ ```
499
545
 
500
- Which is both more concise *and* more representitave of our intention to select only the array.
546
+ By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
@@ -47,7 +47,7 @@ class Evaluator:
47
47
 
48
48
  composite_keys = updated_keys
49
49
 
50
- assert len(composite_keys) == len(items), f'❌ {len(items)} returned for {len(composite_keys)} keys. Are you sure you used a range ([*]) selector?'
50
+ assert len(composite_keys) == len(items), f'❌ {len(items)} matches returned for {len(composite_keys)} keys. Are you sure you used a range ([*]) selector?'
51
51
 
52
52
  results: list[tuple[str, Data]] = []
53
53
  for idx, item in enumerate(list(items)):
@@ -146,7 +146,7 @@ class Token:
146
146
  (idx, item)
147
147
  for idx, item in enumerate(node)
148
148
  if self.selector.match(item) or (
149
- isinstance(item, dict, list)
149
+ isinstance(item, (dict, list))
150
150
  and any([
151
151
  self.selector.match(val)
152
152
  for val in item
@@ -155,8 +155,8 @@ class Token:
155
155
  ]
156
156
 
157
157
  return (
158
- [str(idx) for idx in matches],
159
- [item for item in matches]
158
+ [str(idx) for idx, _ in matches],
159
+ [item for _, item in matches]
160
160
  )
161
161
 
162
162
  def _match_unbound_range(
@@ -185,14 +185,29 @@ class Token:
185
185
  ) for idx, value in enumerate(node) if (
186
186
  str(value) == self.selector
187
187
  ) or (
188
- isinstance(value, dict, list)
189
- and value in self.selector
188
+ isinstance(value, (dict, list))
189
+ and self.selector in value
190
190
  )
191
191
  ]
192
192
 
193
+ for idx, match in enumerate(matches):
194
+ match_idx, item = match
195
+
196
+ if isinstance(item, dict):
197
+ matches[idx] = (
198
+ match_idx,
199
+ item.get(self.selector),
200
+ )
201
+
202
+ elif isinstance(item, list):
203
+ match_idx[idx] = (
204
+ match_idx,
205
+ matches[match_idx],
206
+ )
207
+
193
208
  return (
194
- [str(idx) for idx in matches],
195
- [item for item in matches]
209
+ [str(idx) for idx, _ in matches],
210
+ [item for _, item in matches]
196
211
  )
197
212
 
198
213
  def _match_nested_range(
@@ -243,7 +258,10 @@ class Token:
243
258
  return None, None
244
259
 
245
260
  if isinstance(node, dict):
246
- return ['*'], node.values()
261
+ return (
262
+ ['*' for _ in node],
263
+ node.values()
264
+ )
247
265
 
248
266
  elif isinstance(node, list):
249
267
  return (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfn-check
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Validate Cloud Formation
5
5
  Author-email: Ada Lundhe <adalundhe@lundhe.audio>
6
6
  License: MIT License
@@ -50,7 +50,7 @@ Dynamic: license-file
50
50
 
51
51
  | Package | cfn-check |
52
52
  | ----------- | ----------- |
53
- | Version | 0.2.0 |
53
+ | Version | 0.3.3 |
54
54
  | Download | https://pypi.org/project/cfn-check/ |
55
55
  | Source | https://github.com/adalundhe/cfn-check |
56
56
  | Keywords | cloud-formation, testing, aws, cli |
@@ -314,7 +314,7 @@ class ValidateSecurityGroups(Collection):
314
314
  "Resources::SecurityGroup::Properties::(SecurityGroup)",
315
315
  "It checks Security Groups are correctly definined",
316
316
  )
317
- def validate_test(self, value: list[dict]):
317
+ def validate_security_groups(self, value: list[dict]):
318
318
  assert len(value) > 0
319
319
 
320
320
  for item in value:
@@ -352,7 +352,7 @@ To select all `Resource` objects, then further extract the `Type` field from eac
352
352
  Ranges allow you to perform sophisticated selection of objects or data within a CloudFormation document.
353
353
 
354
354
  > [!IMPORTANT]
355
- > Range Tokens *only* work on list items. This means that any
355
+ > Range Tokens *only* work on arrays. This means that any
356
356
  > values or other objects/data in the selected section of the
357
357
  > CloudFormation document will be *ignored* and filtered out.
358
358
 
@@ -456,7 +456,7 @@ The Rule will fail as below:
456
456
  error: ❌ No results matching results for query Resources::[*]::Type
457
457
  ```
458
458
 
459
- As we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
459
+ as we're selecting objects, not an array! A valid use would be in validating the deeply nested zipfile code of a Lambda's `AppendItemToListFunction`:
460
460
 
461
461
  ```yaml
462
462
  AppendItemToListFunction:
@@ -514,7 +514,7 @@ will select any EC2 ImageIds that start with either `AWSRegion` or `Custom`.
514
514
 
515
515
  ### Nested Ranges
516
516
 
517
- CloudFormation often involes nested arrays, and navigates these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
517
+ CloudFormation often involes nested arrays, and navigating these can make for long and difficult-to-read Queries. To help reduce Query length, `cfn-check` supports nesting Range Tokens. For example, when evaluating:
518
518
 
519
519
  ```yaml
520
520
  ZipFile: !Join
@@ -537,5 +537,51 @@ With Nested Ranges, this can be shortened to:
537
537
 
538
538
  ```
539
539
  Resources::AppendItemToListFunction::Properties::Code::ZipFile::[[]]
540
+ ```
540
541
 
541
542
  Which is both more concise *and* more representitave of our intention to select only the array.
543
+
544
+ <br/>
545
+
546
+ # Using Pydantic Models
547
+
548
+ In addition to traditional `pytest`-like assert statements, `cfn-lint` can validate results returned by queries via `Pydantic` models.
549
+
550
+ For example, consider again the initial example where we validate the `Type` field of `Resource` objects.
551
+
552
+ ```python
553
+ from cfn_check import Collection, Rule
554
+
555
+
556
+ class ValidateResourceType(Collection):
557
+
558
+ @Rule(
559
+ "Resources::*::Type",
560
+ "It checks Resource::Type is correctly definined",
561
+ )
562
+ def validate_test(self, value: str):
563
+ assert value is not None, '❌ Resource Type not defined'
564
+ assert isinstance(value, str), '❌ Resource Type not a string'
565
+ ```
566
+
567
+ Rather than explicitly querying for the type field and writing assertions, we can instead define a `Pydantic` schema, then pass all `Resource` objects to that schema by specifying it as a Python type hint in our `Rule` method's signature.
568
+
569
+ ```python
570
+ from cfn_check import Collection, Rule
571
+ from pydantic import BaseModel, StrictStr
572
+
573
+ class Resource(BaseModel):
574
+ Type: StrictStr
575
+
576
+
577
+ class ValidateResourceType(Collection):
578
+
579
+ @Rule(
580
+ "Resources::*",
581
+ "It checks Resource::Type is correctly definined",
582
+ )
583
+ def validate_test(self, value: Resource):
584
+ assert value is not None
585
+ ```
586
+
587
+ By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
@@ -34,4 +34,5 @@ cfn_check/shared/__init__.py
34
34
  cfn_check/shared/types.py
35
35
  cfn_check/validation/__init__.py
36
36
  cfn_check/validation/validator.py
37
+ example/pydantic_rules.py
37
38
  example/rules.py
@@ -0,0 +1,15 @@
1
+ from cfn_check import Collection, Rule
2
+ from pydantic import BaseModel, StrictStr
3
+
4
+ class Resource(BaseModel):
5
+ Type: StrictStr
6
+
7
+
8
+ class ValidateResourceType(Collection):
9
+
10
+ @Rule(
11
+ "Resources::*",
12
+ "It checks Resource::Type is correctly definined",
13
+ )
14
+ def validate_test(self, value: Resource):
15
+ assert value is not None
@@ -41,7 +41,7 @@ class ResourcesChecks(Collection):
41
41
  "Resources::SecurityGroup::Properties::(SecurityGroup)::[]",
42
42
  "It checks Security Groups are correctly definined",
43
43
  )
44
- def validate_test(self, value: list[dict]):
44
+ def validate_security_groups(self, value: list[dict]):
45
45
  assert len(value) > 0
46
46
 
47
47
  for item in value:
@@ -68,4 +68,13 @@ class ResourcesChecks(Collection):
68
68
  def validate_lambda_code_zipfile(self, value: list[str]):
69
69
  assert value is not None, '❌ Lambda zipfile code is not defined'
70
70
  assert isinstance(value, list), '❌ Lambda execution zipfile code not a list'
71
- assert len(value) > 0,'❌ Lambda execution zipfile code empty'
71
+ assert len(value) > 0,'❌ Lambda execution zipfile code empty'
72
+
73
+ @Rule(
74
+ "Resources::SecurityGroup::Properties::(SecurityGroup)::[IpProtocol]",
75
+ "It checks Security Groups IpProtocols are tcp",
76
+ )
77
+ def validate_ip_protocols(self, ip_protocol: str):
78
+ assert ip_protocol is not None
79
+ assert isinstance(ip_protocol, str)
80
+ assert ip_protocol == "tcp"
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "cfn-check"
9
- version = "0.3.1"
9
+ version = "0.3.3"
10
10
  requires-python = ">=3.12"
11
11
  description = "Validate Cloud Formation"
12
12
  readme = "README.md"
File without changes
File without changes