modelmoat 0.1.0__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.
- modelmoat-0.1.0/.github/workflows/ci.yml +47 -0
- modelmoat-0.1.0/.gitignore +14 -0
- modelmoat-0.1.0/CLAUDE.md +117 -0
- modelmoat-0.1.0/CONTRIBUTING.md +29 -0
- modelmoat-0.1.0/LICENSE +202 -0
- modelmoat-0.1.0/PKG-INFO +170 -0
- modelmoat-0.1.0/README.md +142 -0
- modelmoat-0.1.0/SECURITY.md +16 -0
- modelmoat-0.1.0/modelmoat/__init__.py +3 -0
- modelmoat-0.1.0/modelmoat/checks/__init__.py +15 -0
- modelmoat-0.1.0/modelmoat/checks/datastores.py +270 -0
- modelmoat-0.1.0/modelmoat/checks/iam.py +178 -0
- modelmoat-0.1.0/modelmoat/checks/network.py +148 -0
- modelmoat-0.1.0/modelmoat/checks/s3.py +182 -0
- modelmoat-0.1.0/modelmoat/checks/sagemaker.py +58 -0
- modelmoat-0.1.0/modelmoat/cli.py +159 -0
- modelmoat-0.1.0/modelmoat/graph.py +235 -0
- modelmoat-0.1.0/modelmoat/policy.py +179 -0
- modelmoat-0.1.0/modelmoat/scanner.py +121 -0
- modelmoat-0.1.0/pyproject.toml +46 -0
- modelmoat-0.1.0/tests/fixtures/insecure/iam_bad.tf +30 -0
- modelmoat-0.1.0/tests/fixtures/insecure/lambda_bad.tf +26 -0
- modelmoat-0.1.0/tests/fixtures/insecure/s3_bad.tf +26 -0
- modelmoat-0.1.0/tests/fixtures/insecure/sagemaker_bad.tf +7 -0
- modelmoat-0.1.0/tests/fixtures/insecure/unrelated_bad.tf +8 -0
- modelmoat-0.1.0/tests/fixtures/insecure/vector_bad.tf +20 -0
- modelmoat-0.1.0/tests/fixtures/secure/iam.tf +19 -0
- modelmoat-0.1.0/tests/fixtures/secure/lambda.tf +15 -0
- modelmoat-0.1.0/tests/fixtures/secure/main.tf +3 -0
- modelmoat-0.1.0/tests/fixtures/secure/network.tf +10 -0
- modelmoat-0.1.0/tests/fixtures/secure/s3.tf +6 -0
- modelmoat-0.1.0/tests/fixtures/secure/sagemaker.tf +26 -0
- modelmoat-0.1.0/tests/fixtures/secure/security.tf +7 -0
- modelmoat-0.1.0/tests/fixtures/secure/unrelated.tf +17 -0
- modelmoat-0.1.0/tests/fixtures/secure/vector.tf +26 -0
- modelmoat-0.1.0/tests/test_modelmoat.py +206 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: ruff check modelmoat tests
|
|
31
|
+
|
|
32
|
+
- name: Unit and fixture tests
|
|
33
|
+
run: pytest -q
|
|
34
|
+
|
|
35
|
+
# The two-sided behavior gate. A scanner that flags secure infrastructure
|
|
36
|
+
# is as broken as one that misses insecure infrastructure, so CI requires
|
|
37
|
+
# zero findings on the secure fixture and a failing exit code on the
|
|
38
|
+
# insecure one.
|
|
39
|
+
- name: Behavior gate - secure fixture must be clean
|
|
40
|
+
run: modelmoat scan tests/fixtures/secure
|
|
41
|
+
|
|
42
|
+
- name: Behavior gate - insecure fixture must fail the build
|
|
43
|
+
run: |
|
|
44
|
+
if modelmoat scan tests/fixtures/insecure --json > /dev/null; then
|
|
45
|
+
echo "Expected a non-zero exit code on the insecure fixture"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Context for Claude Code sessions in this repository. Read this before making
|
|
4
|
+
changes.
|
|
5
|
+
|
|
6
|
+
## What this project is
|
|
7
|
+
|
|
8
|
+
modelmoat is a static analysis tool that scans Terraform for AI infrastructure
|
|
9
|
+
security misconfigurations: SageMaker networking, Bedrock and SageMaker IAM
|
|
10
|
+
grants, model artifact buckets, missing PrivateLink for AI service traffic, and
|
|
11
|
+
vector data store posture.
|
|
12
|
+
|
|
13
|
+
Positioning, stated exactly this way and no stronger: general IaC scanners cover
|
|
14
|
+
some of this ground, and modelmoat is the one that treats AI infrastructure as
|
|
15
|
+
its own category. Never claim to be the first or only scanner with AI checks.
|
|
16
|
+
That claim is false and the audience for this tool will check.
|
|
17
|
+
|
|
18
|
+
The maintainer works in infrastructure security and is not a Terraform expert.
|
|
19
|
+
Explain Terraform and AWS specifics when they matter to a decision, and do not
|
|
20
|
+
assume familiarity with provider internals.
|
|
21
|
+
|
|
22
|
+
## The one rule above all others
|
|
23
|
+
|
|
24
|
+
The secure fixture must produce zero findings. A scanner that fires on correct
|
|
25
|
+
infrastructure trains people to ignore it, and for this project the false
|
|
26
|
+
positive rate is the product. CI enforces this in both directions on every push:
|
|
27
|
+
`tests/fixtures/secure` must exit 0, `tests/fixtures/insecure` must exit
|
|
28
|
+
non-zero.
|
|
29
|
+
|
|
30
|
+
If a change makes the secure fixture produce a finding, the change is wrong. Do
|
|
31
|
+
not adjust the fixture to make a check pass.
|
|
32
|
+
|
|
33
|
+
## Rules every check follows
|
|
34
|
+
|
|
35
|
+
1. Checks run against the whole `ProjectGraph`, never a single file. Terraform
|
|
36
|
+
spreads related resources across files and so do we.
|
|
37
|
+
2. Values containing `${` are unknown and are never flagged. modelmoat does not
|
|
38
|
+
flag what it cannot prove.
|
|
39
|
+
3. Keyword matching is on whole tokens, never substrings. `email` must not match
|
|
40
|
+
`ai` and `html` must not match `ml`. Both are permanent negative controls in
|
|
41
|
+
the test suite.
|
|
42
|
+
4. Severity is calibrated to demonstrated exposure:
|
|
43
|
+
- CRITICAL: the configuration itself proves internet reachability with weak
|
|
44
|
+
or absent authentication, for example a bucket policy granting
|
|
45
|
+
`Principal "*"`.
|
|
46
|
+
- HIGH: missing encryption, or permissions broad enough to reach any AI
|
|
47
|
+
resource in the account.
|
|
48
|
+
- MEDIUM and LOW: hygiene. A missing S3 public access block is LOW, because
|
|
49
|
+
AWS account defaults have blocked public access on new buckets since April
|
|
50
|
+
2023.
|
|
51
|
+
5. Finding messages never claim more than the configuration proves. A SageMaker
|
|
52
|
+
model outside a VPC is a runtime and traffic path exposure, not an open URL,
|
|
53
|
+
because invocation still requires a SigV4-signed IAM request. Say so.
|
|
54
|
+
6. Every new check needs two fixtures: one that triggers it, and one that looks
|
|
55
|
+
similar but must stay silent.
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
- `modelmoat/graph.py` parses every `.tf` file once into a `ProjectGraph`.
|
|
60
|
+
Handles python-hcl2 8.x quirks: string literals and block labels come back
|
|
61
|
+
wrapped in literal quotes, and nested blocks come back as lists of dicts
|
|
62
|
+
tagged with `__is_block__`. Helpers: `unquote`, `normalize`, `blocks`,
|
|
63
|
+
`first_block`, `is_unknown`, `truthy`, `missing_or_false`, `ai_tokens_in`,
|
|
64
|
+
`extract_ref`.
|
|
65
|
+
- `modelmoat/policy.py` parses IAM policy documents. Terraform expresses these
|
|
66
|
+
four ways: `jsonencode()` (which hcl2 serializes back in HCL syntax with `=`
|
|
67
|
+
instead of `:`, so it needs a transform before JSON parsing), raw JSON
|
|
68
|
+
heredocs, `data.aws_iam_policy_document` statement blocks, and managed policy
|
|
69
|
+
ARNs. Managed policy matching is case-insensitive on both sides.
|
|
70
|
+
- `modelmoat/scanner.py` holds `Finding`, `ScanResult`, and the orchestrator.
|
|
71
|
+
- `modelmoat/checks/` holds the five checks, registered in `checks/__init__.py`.
|
|
72
|
+
- `modelmoat/cli.py` is Typer with an explicit `scan` subcommand. Exit code is
|
|
73
|
+
computed before severity filtering, so `--min-severity` changes what prints
|
|
74
|
+
without changing what fails a build.
|
|
75
|
+
|
|
76
|
+
## Gotchas already paid for
|
|
77
|
+
|
|
78
|
+
- python-hcl2 8.x does not support the `with_meta` keyword, so line numbers come
|
|
79
|
+
from a regex scan of the source, not the parser.
|
|
80
|
+
- `vpc_config` lives on `aws_sagemaker_model`, not on
|
|
81
|
+
`aws_sagemaker_endpoint_configuration`. Checking the endpoint config fires on
|
|
82
|
+
every endpoint config ever written.
|
|
83
|
+
- S3 public access block arguments default to false when omitted, not true.
|
|
84
|
+
- Block Public Access overrides both ACLs and bucket policies, so a bucket with
|
|
85
|
+
all four protections enabled produces no finding regardless of its ACL.
|
|
86
|
+
- CLI output uses a per-finding block layout, not a Rich table. Tables shred at
|
|
87
|
+
80 columns, which is what people see when piping output to a file.
|
|
88
|
+
|
|
89
|
+
## Before committing
|
|
90
|
+
|
|
91
|
+
python -m pytest -q
|
|
92
|
+
python -m ruff check modelmoat tests
|
|
93
|
+
|
|
94
|
+
Both must pass. Do not commit with failing tests or lint errors.
|
|
95
|
+
|
|
96
|
+
## Writing style for anything user-facing
|
|
97
|
+
|
|
98
|
+
Applies to README, docs, finding messages, release notes, issue replies, and any
|
|
99
|
+
LinkedIn or blog drafts.
|
|
100
|
+
|
|
101
|
+
Banned words: leverage, navigate, robust, seamless, foster, delve, landscape,
|
|
102
|
+
tapestry, vibrant, nestled, underscore, harness, unleash, unlock, elevate,
|
|
103
|
+
empower, streamline, pivotal, paradigm, holistic, synergy, ecosystem, journey,
|
|
104
|
+
realm, sphere, arena, fabric, beacon, cornerstone, testament.
|
|
105
|
+
|
|
106
|
+
Banned phrases: "not just X but Y", "it's important to note", "furthermore",
|
|
107
|
+
"moreover", "in conclusion".
|
|
108
|
+
|
|
109
|
+
Style: no em dashes. Lead with specific numbers. Paragraphs of three or four
|
|
110
|
+
sentences at most. No rule-of-three lists. No emojis in headers. No stacked
|
|
111
|
+
hedging. Never name the maintainer's employer.
|
|
112
|
+
|
|
113
|
+
## Roadmap
|
|
114
|
+
|
|
115
|
+
Pinecone and Weaviate providers, SARIF output for GitHub code scanning, Azure AI
|
|
116
|
+
and Vertex AI resources, and a `--baseline` file so teams can adopt the tool on
|
|
117
|
+
an existing codebase without a wall of findings on day one.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are welcome, especially new checks and false positive reports.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
pip install -e ".[dev]"
|
|
8
|
+
pytest
|
|
9
|
+
|
|
10
|
+
## Rules for checks
|
|
11
|
+
|
|
12
|
+
1. Every check runs against the whole project graph, not a single file.
|
|
13
|
+
Terraform spreads related resources across files and so do we.
|
|
14
|
+
2. Every check needs two fixtures: one that triggers it and one that looks
|
|
15
|
+
similar but must not. The secure fixture returning zero findings is a hard
|
|
16
|
+
CI gate.
|
|
17
|
+
3. Severity is calibrated to real exposure. Reachable-from-internet with weak
|
|
18
|
+
or no auth is CRITICAL. Missing encryption or broad IAM is HIGH. Hygiene
|
|
19
|
+
gaps are MEDIUM or LOW. A finding message must never claim more than the
|
|
20
|
+
configuration proves.
|
|
21
|
+
4. Values that come from variables or expressions we cannot resolve are
|
|
22
|
+
treated as unknown and are not flagged.
|
|
23
|
+
|
|
24
|
+
## Adding a check
|
|
25
|
+
|
|
26
|
+
Create a module in `modelmoat/checks/`, implement a class with `check_id`,
|
|
27
|
+
`check_name`, and `run(graph) -> list[Finding]`, register it in
|
|
28
|
+
`modelmoat/checks/__init__.py`, and add both fixtures plus assertions in
|
|
29
|
+
`tests/`.
|
modelmoat-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
modelmoat-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: modelmoat
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Static analysis for AI infrastructure security in Terraform. Finds misconfigured SageMaker, Bedrock IAM, model artifact buckets, missing PrivateLink, and exposed vector databases before they deploy.
|
|
5
|
+
Project-URL: Homepage, https://github.com/rashadlee/modelmoat
|
|
6
|
+
Project-URL: Issues, https://github.com/rashadlee/modelmoat/issues
|
|
7
|
+
Author: modelmoat contributors
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ai-security,bedrock,iac,sagemaker,security,static-analysis,terraform
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: python-hcl2<9,>=8.0
|
|
22
|
+
Requires-Dist: rich>=13.0
|
|
23
|
+
Requires-Dist: typer>=0.12
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# modelmoat
|
|
30
|
+
|
|
31
|
+
Static analysis for AI infrastructure security in Terraform. It reads your `.tf`
|
|
32
|
+
files and finds the misconfigurations that show up specifically when teams ship
|
|
33
|
+
Bedrock, SageMaker, and vector databases: blanket `bedrock:*` grants, model
|
|
34
|
+
artifact buckets open to the internet, embedding stores without encryption, and
|
|
35
|
+
inference traffic that never touches your private network.
|
|
36
|
+
|
|
37
|
+
General IaC scanners check hundreds of AWS resource types and cover some of this
|
|
38
|
+
ground. modelmoat is the one that treats AI infrastructure as its own category,
|
|
39
|
+
with checks written around how these services actually fail rather than a
|
|
40
|
+
generic encryption rule applied to every database in the account.
|
|
41
|
+
|
|
42
|
+
Findings run against the whole project at once, not file by file, because
|
|
43
|
+
Terraform spreads related resources across files. A bucket in `s3.tf` and its
|
|
44
|
+
public access block in `security.tf` are one decision, and modelmoat reads them
|
|
45
|
+
that way.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
pipx install modelmoat
|
|
50
|
+
|
|
51
|
+
Or with pip:
|
|
52
|
+
|
|
53
|
+
pip install modelmoat
|
|
54
|
+
|
|
55
|
+
Python 3.10 or newer. modelmoat only reads local files. It never contacts your
|
|
56
|
+
AWS account, reads state files, or touches credentials.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
modelmoat scan .
|
|
61
|
+
modelmoat scan infra/ modules/ --min-severity HIGH
|
|
62
|
+
modelmoat scan . --json > findings.json
|
|
63
|
+
|
|
64
|
+
Real output from the test fixtures in this repo:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
modelmoat 0.1.0 scanned 6 Terraform file(s)
|
|
68
|
+
CRITICAL: 4 HIGH: 7 MEDIUM: 4 LOW: 3
|
|
69
|
+
|
|
70
|
+
CRITICAL S3-001 aws_s3_bucket.datasets
|
|
71
|
+
tests/fixtures/insecure/s3_bad.tf:11
|
|
72
|
+
S3 bucket 'datasets' looks AI/ML related (matched: datasets) and its
|
|
73
|
+
bucket policy allows Principal "*". Anyone on the internet can perform
|
|
74
|
+
the granted actions.
|
|
75
|
+
fix: Restrict the policy principal to specific roles or accounts and
|
|
76
|
+
add an aws_s3_bucket_public_access_block with all four protections
|
|
77
|
+
enabled.
|
|
78
|
+
|
|
79
|
+
HIGH IAM-001 aws_iam_role_policy_attachment.full_access
|
|
80
|
+
tests/fixtures/insecure/iam_bad.tf:12
|
|
81
|
+
Role 'agent_role' attaches AWS managed policy
|
|
82
|
+
'arn:aws:iam::aws:policy/AmazonBedrockFullAccess', which grants blanket
|
|
83
|
+
AI service access. The role is used by Lambda function(s) public_agent,
|
|
84
|
+
vpc_agent.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Checks
|
|
88
|
+
|
|
89
|
+
| ID | What it finds | Severity range |
|
|
90
|
+
|----|---------------|----------------|
|
|
91
|
+
| SMK-001 | SageMaker models with no `vpc_config`, so containers run on the managed network with direct internet egress | HIGH |
|
|
92
|
+
| IAM-001 | Wildcard AI grants (`bedrock:*`, `sagemaker:*` on `Resource "*"`) in inline policies, customer managed policies, policy documents, or attached AWS `FullAccess` policies | HIGH |
|
|
93
|
+
| S3-001 | AI-related buckets exposed by a public ACL or a `Principal "*"` policy, plus weakened or missing public access blocks | CRITICAL to LOW |
|
|
94
|
+
| VPC-001 | Lambda functions calling Bedrock or SageMaker with no matching interface VPC endpoint in the project | MEDIUM to LOW |
|
|
95
|
+
| VEC-001 | OpenSearch, pgvector-capable Postgres, and AI-named ElastiCache missing encryption or network isolation | CRITICAL to LOW |
|
|
96
|
+
|
|
97
|
+
## Exit codes and CI
|
|
98
|
+
|
|
99
|
+
`modelmoat scan` exits 1 when findings at or above `--fail-on` exist, and 0
|
|
100
|
+
otherwise. The default is `HIGH`, so hygiene findings do not break builds.
|
|
101
|
+
Exit code 2 means bad arguments.
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
- name: Scan AI infrastructure
|
|
105
|
+
run: |
|
|
106
|
+
pip install modelmoat
|
|
107
|
+
modelmoat scan infra/ --fail-on HIGH
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Tighten with `--fail-on MEDIUM` once your baseline is clean, or loosen to
|
|
111
|
+
`CRITICAL` while you work through a backlog. `--min-severity` controls what gets
|
|
112
|
+
printed and is separate from what fails the build, so you can see everything
|
|
113
|
+
while only blocking on the serious findings.
|
|
114
|
+
|
|
115
|
+
## How severity is decided
|
|
116
|
+
|
|
117
|
+
CRITICAL means the configuration itself proves internet reachability with weak
|
|
118
|
+
or absent authentication. A bucket policy granting `Principal "*"` qualifies. A
|
|
119
|
+
SageMaker model outside a VPC does not, because invoking it still requires a
|
|
120
|
+
SigV4-signed IAM request, and modelmoat says exactly that in the finding rather
|
|
121
|
+
than claiming anyone with the URL can hit your model.
|
|
122
|
+
|
|
123
|
+
HIGH covers missing encryption and permissions broad enough to reach any AI
|
|
124
|
+
resource in the account. MEDIUM and LOW are hygiene: a missing public access
|
|
125
|
+
block is LOW, since account defaults have blocked public access on new buckets
|
|
126
|
+
since April 2023, and calling it CRITICAL would be wrong.
|
|
127
|
+
|
|
128
|
+
## Design notes
|
|
129
|
+
|
|
130
|
+
Values that come from variables or expressions are unknown, and modelmoat does
|
|
131
|
+
not flag what it cannot prove. `storage_encrypted = var.encrypt_storage` produces
|
|
132
|
+
no finding either way.
|
|
133
|
+
|
|
134
|
+
Keyword matching is on whole tokens, never substrings. A bucket named
|
|
135
|
+
`email-archive` does not match `ai`, and `html-assets` does not match `ml`. Both
|
|
136
|
+
appear in the test suite as negative controls that must stay silent.
|
|
137
|
+
|
|
138
|
+
The test suite has one rule above all others: the secure fixture must produce
|
|
139
|
+
zero findings. A scanner that fires on correct infrastructure trains people to
|
|
140
|
+
ignore it, so CI runs both directions on every push, requiring a clean pass on
|
|
141
|
+
nine files of best-practice Terraform and a failing exit code on the insecure
|
|
142
|
+
fixture.
|
|
143
|
+
|
|
144
|
+
## Limitations
|
|
145
|
+
|
|
146
|
+
modelmoat reads HCL statically. It does not evaluate modules, resolve variable
|
|
147
|
+
files, expand `for_each`, or read remote state, so a security control defined in
|
|
148
|
+
a module that this project only calls will not be seen. Coverage is AWS only
|
|
149
|
+
today, and provider-specific vector databases like Pinecone and Weaviate are not
|
|
150
|
+
yet checked.
|
|
151
|
+
|
|
152
|
+
Static analysis cannot tell you whether a security group actually permits the
|
|
153
|
+
traffic you fear, or whether an IAM permission is used. Treat findings as places
|
|
154
|
+
to look, not verdicts.
|
|
155
|
+
|
|
156
|
+
## Roadmap
|
|
157
|
+
|
|
158
|
+
Pinecone and Weaviate providers, SARIF output for GitHub code scanning, Azure AI
|
|
159
|
+
and Vertex AI resources, and a `--baseline` file for adopting the tool on an
|
|
160
|
+
existing codebase without a wall of findings on day one.
|
|
161
|
+
|
|
162
|
+
## Contributing
|
|
163
|
+
|
|
164
|
+
False positive reports are as welcome as new checks and get the same priority.
|
|
165
|
+
See CONTRIBUTING.md for the rules every check follows and SECURITY.md for
|
|
166
|
+
reporting a vulnerability in the tool itself.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
Apache-2.0.
|