modmanifest 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.
- modmanifest-0.1.0/.gitignore +15 -0
- modmanifest-0.1.0/CHANGELOG.md +11 -0
- modmanifest-0.1.0/CONTRIBUTING.md +13 -0
- modmanifest-0.1.0/LICENSE +21 -0
- modmanifest-0.1.0/PKG-INFO +176 -0
- modmanifest-0.1.0/README.md +145 -0
- modmanifest-0.1.0/SECURITY.md +16 -0
- modmanifest-0.1.0/pyproject.toml +68 -0
- modmanifest-0.1.0/src/modmanifest/__init__.py +4 -0
- modmanifest-0.1.0/src/modmanifest/__main__.py +4 -0
- modmanifest-0.1.0/src/modmanifest/archive/__init__.py +1 -0
- modmanifest-0.1.0/src/modmanifest/archive/limits.py +36 -0
- modmanifest-0.1.0/src/modmanifest/archive/signatures.py +47 -0
- modmanifest-0.1.0/src/modmanifest/archive/sources.py +196 -0
- modmanifest-0.1.0/src/modmanifest/archive/zip_inspector.py +302 -0
- modmanifest-0.1.0/src/modmanifest/cli.py +172 -0
- modmanifest-0.1.0/src/modmanifest/codes.py +95 -0
- modmanifest-0.1.0/src/modmanifest/context.py +60 -0
- modmanifest-0.1.0/src/modmanifest/games/__init__.py +1 -0
- modmanifest-0.1.0/src/modmanifest/games/base.py +22 -0
- modmanifest-0.1.0/src/modmanifest/games/dbpf.py +167 -0
- modmanifest-0.1.0/src/modmanifest/games/inzoi.py +345 -0
- modmanifest-0.1.0/src/modmanifest/games/sims4.py +171 -0
- modmanifest-0.1.0/src/modmanifest/inspector.py +112 -0
- modmanifest-0.1.0/src/modmanifest/metadata/__init__.py +1 -0
- modmanifest-0.1.0/src/modmanifest/metadata/ai_audit.py +375 -0
- modmanifest-0.1.0/src/modmanifest/metadata/ai_signatures.py +77 -0
- modmanifest-0.1.0/src/modmanifest/metadata/parsers.py +158 -0
- modmanifest-0.1.0/src/modmanifest/models.py +200 -0
- modmanifest-0.1.0/src/modmanifest/py.typed +0 -0
- modmanifest-0.1.0/src/modmanifest/reporting.py +80 -0
- modmanifest-0.1.0/tests/__init__.py +0 -0
- modmanifest-0.1.0/tests/helpers.py +89 -0
- modmanifest-0.1.0/tests/test_ai_metadata.py +150 -0
- modmanifest-0.1.0/tests/test_archives.py +121 -0
- modmanifest-0.1.0/tests/test_cli.py +140 -0
- modmanifest-0.1.0/tests/test_inzoi.py +131 -0
- modmanifest-0.1.0/tests/test_sims4.py +112 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
- `inspect`, `validate` and `audit-ai` commands with stable JSON output (schema version 1).
|
|
8
|
+
- Archive and folder safety checks with configurable limits.
|
|
9
|
+
- The Sims 4: DBPF 2.1 structure and `.ts4script` archive checks.
|
|
10
|
+
- inZOI: candidate-package manifest inspection and observation-based checks.
|
|
11
|
+
- AI metadata audit: manifests, PNG text chunks, XMP, EXIF; C2PA detection only.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
1. Fork, branch, and keep changes focused.
|
|
4
|
+
2. `pip install -e ".[dev]"`
|
|
5
|
+
3. `ruff check . && ruff format --check . && pytest` must pass.
|
|
6
|
+
|
|
7
|
+
Guidelines:
|
|
8
|
+
|
|
9
|
+
- Tests use synthetic fixtures built in code. Never commit real mod content.
|
|
10
|
+
- Every emitted diagnostic code must be registered in `src/modmanifest/codes.py` with a
|
|
11
|
+
basis. Keep observed conventions separate from verified format rules.
|
|
12
|
+
- The tool is read-only: no extraction, execution, network access or file modification.
|
|
13
|
+
- Do not guess unknown formats or checksum algorithms; report `NOT CHECKED`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CyberStudios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: modmanifest
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Read-only inspection, validation and metadata auditing for game mod packages (The Sims 4, inZOI).
|
|
5
|
+
Project-URL: Homepage, https://github.com/CyberStudiosTM/ModManifest
|
|
6
|
+
Project-URL: Issues, https://github.com/CyberStudiosTM/ModManifest/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/CyberStudiosTM/ModManifest/blob/main/CHANGELOG.md
|
|
8
|
+
Author: CyberStudios
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: audit,cli,inzoi,metadata,mods,sims4
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: defusedxml>=0.7.1
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: rich>=13.7
|
|
26
|
+
Requires-Dist: typer>=0.12
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# ModManifest
|
|
33
|
+
|
|
34
|
+
Read-only inspection, validation and metadata auditing for game mod packages.
|
|
35
|
+
Supports **The Sims 4** and **inZOI**. Part of [CyberStudios](https://github.com/CyberStudiosTM).
|
|
36
|
+
|
|
37
|
+
ModManifest never extracts, executes, modifies or uploads anything. It reports what it can
|
|
38
|
+
verify, what it only observed, and what it did not check.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
pip install modmanifest
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Requires Python 3.11+. From source: `pip install .`
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
modmanifest inspect PATH [--json]
|
|
52
|
+
modmanifest validate PATH --game sims4|inzoi [--json]
|
|
53
|
+
modmanifest audit-ai PATH [--json]
|
|
54
|
+
modmanifest --version
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`PATH` is a folder, `.zip`, `.ts4script` or `.package`.
|
|
58
|
+
|
|
59
|
+
| Command | Runs |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `inspect` | archive safety, game recognition and validation, AI metadata audit |
|
|
62
|
+
| `validate` | archive safety and the selected game's validation |
|
|
63
|
+
| `audit-ai` | archive safety and the AI metadata audit |
|
|
64
|
+
|
|
65
|
+
Options: `--json`, `--verbose`, `--timeout`, `--max-entries`, `--max-total-mb`, `--max-entry-mb`.
|
|
66
|
+
|
|
67
|
+
### Statuses
|
|
68
|
+
|
|
69
|
+
`PASS`, `WARNING`, `ERROR`, `UNSUPPORTED`, `NOT CHECKED` (`NOT_CHECKED` in JSON).
|
|
70
|
+
Every finding carries a stable code (`MM-ARC-*`, `MM-SCN-*`, `MM-S4-*`, `MM-INZ-*`) and a
|
|
71
|
+
basis: `format-rule`, `observed-convention`, `safety-limit` or `scan-coverage`.
|
|
72
|
+
|
|
73
|
+
### Exit codes
|
|
74
|
+
|
|
75
|
+
| Code | Meaning |
|
|
76
|
+
|---|---|
|
|
77
|
+
| 0 | scan complete, no `ERROR` findings |
|
|
78
|
+
| 1 | one or more `ERROR` findings |
|
|
79
|
+
| 2 | invalid arguments or missing path |
|
|
80
|
+
| 3 | incomplete scan (limit, timeout, read failure), or `validate` recognized nothing |
|
|
81
|
+
|
|
82
|
+
AI indicators never change the exit code.
|
|
83
|
+
|
|
84
|
+
## Example
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
$ modmanifest validate SampleMod --game inzoi
|
|
88
|
+
ModManifest validate SampleMod
|
|
89
|
+
target: folder scan: complete
|
|
90
|
+
game selected: inzoi recognized: inzoi
|
|
91
|
+
Status Code Finding
|
|
92
|
+
NOT_CHECKED MM-INZ-015 no official inZOI package specification was used; ...
|
|
93
|
+
NOT_CHECKED MM-INZ-013 declared dependencies are reported as written; ...
|
|
94
|
+
NOT_CHECKED MM-INZ-017 game version metadata is reported as written; ...
|
|
95
|
+
NOT_CHECKED MM-INZ-009 checksum digests were not verified: the algorithm ...
|
|
96
|
+
PASS 2 WARNING 0 ERROR 0 UNSUPPORTED 0 NOT_CHECKED 5
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Validation coverage
|
|
100
|
+
|
|
101
|
+
### The Sims 4
|
|
102
|
+
|
|
103
|
+
- `.package` (DBPF 2.1): signature, header, index bounds and size, resource ranges,
|
|
104
|
+
duplicate keys. Resource contents are **not** decoded.
|
|
105
|
+
- `.ts4script`: opened as a ZIP, `.py`/`.pyc` entries listed, compiled-code header checked.
|
|
106
|
+
Script contents are **not** analyzed or executed.
|
|
107
|
+
- No standard Sims 4 manifest is assumed, so dependencies and versions are not extracted.
|
|
108
|
+
|
|
109
|
+
### inZOI
|
|
110
|
+
|
|
111
|
+
A root `mod_manifest.json` marks a **candidate** mod, not a valid package. There is no
|
|
112
|
+
official specification behind these checks.
|
|
113
|
+
|
|
114
|
+
- JSON syntax and field types, compared with sample packages. Observed fields are never required.
|
|
115
|
+
- Identity, author, version, description, declared dependencies and game-version metadata are
|
|
116
|
+
reported as written. Dependencies and compatibility are **not** verified.
|
|
117
|
+
- `ProjectName` versus folder name is informational.
|
|
118
|
+
- `FileChecksums`: shape, path safety and listed-file existence are checked. Digests are
|
|
119
|
+
**NOT CHECKED**: in sample packages the algorithm and path semantics could not be
|
|
120
|
+
established, and none is guessed. A matching digest would show consistency with the
|
|
121
|
+
manifest only, not that a mod is safe or authentic.
|
|
122
|
+
- `.pak`, `.ucas`, `.utoc`: recognized as Unreal content containers and inventoried;
|
|
123
|
+
internals are not inspected. A lone `.ucas` or `.utoc` is reported as a warning.
|
|
124
|
+
- Unrelated folders (mod-manager state, hosted content) are unrecognized, not invalid.
|
|
125
|
+
Point ModManifest at one mod folder at a time.
|
|
126
|
+
|
|
127
|
+
### Archive and folder safety (all targets)
|
|
128
|
+
|
|
129
|
+
Traversal and absolute paths, drive letters and alternate streams, backslashes, duplicates,
|
|
130
|
+
case collisions, symlinks and special files, entry count, total and per-entry size,
|
|
131
|
+
compression ratio, signature mismatch, encrypted entries (`UNSUPPORTED`), and nested
|
|
132
|
+
archives (listed, not opened). Limits are configurable.
|
|
133
|
+
|
|
134
|
+
## AI metadata audit
|
|
135
|
+
|
|
136
|
+
Reports indicators found in metadata, in three categories: explicit declaration, tool
|
|
137
|
+
reference, ambiguous indicator. Confidence refers to the interpretation of the metadata
|
|
138
|
+
only.
|
|
139
|
+
|
|
140
|
+
Supported sources: JSON, TOML, YAML and XML manifests; text and Markdown notes; PNG text
|
|
141
|
+
chunks; XMP; EXIF `Software`/`ImageDescription`. C2PA is **detection only**: possible
|
|
142
|
+
structures are reported, never parsed or signature-validated.
|
|
143
|
+
|
|
144
|
+
When nothing is found, the result is:
|
|
145
|
+
|
|
146
|
+
> No supported AI metadata indicators found.
|
|
147
|
+
|
|
148
|
+
Metadata may be absent, incomplete or removed. That result is not evidence about how a
|
|
149
|
+
package was made. Prompt text is never copied into reports. Name tables used for matching
|
|
150
|
+
live in `src/modmanifest/metadata/ai_signatures.py` and are detection data only.
|
|
151
|
+
|
|
152
|
+
## JSON output
|
|
153
|
+
|
|
154
|
+
Stable schema, `schema_version: 1`. Top-level keys: `tool`, `version`, `command`, `target`,
|
|
155
|
+
`game`, `scan`, `recognized_files`, `capabilities_exercised`, `package`, `findings`,
|
|
156
|
+
`summary`, `unsupported`, `inspection_errors`, `ai_metadata`. The target is a base name,
|
|
157
|
+
never an absolute path. Untrusted text has control and bidirectional characters escaped.
|
|
158
|
+
|
|
159
|
+
## Limitations
|
|
160
|
+
|
|
161
|
+
- Format checks reflect observed samples and public format knowledge, not vendor specs.
|
|
162
|
+
- Only listed metadata containers are audited; scripts and binary game data are not.
|
|
163
|
+
- 7z, RAR and other archive formats are listed, not opened.
|
|
164
|
+
- Package validity here does not mean a mod is safe, compatible or working in game.
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```powershell
|
|
169
|
+
pip install -e ".[dev]"
|
|
170
|
+
ruff check . ; ruff format --check .
|
|
171
|
+
pytest
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT. Copyright (c) CyberStudios.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# ModManifest
|
|
2
|
+
|
|
3
|
+
Read-only inspection, validation and metadata auditing for game mod packages.
|
|
4
|
+
Supports **The Sims 4** and **inZOI**. Part of [CyberStudios](https://github.com/CyberStudiosTM).
|
|
5
|
+
|
|
6
|
+
ModManifest never extracts, executes, modifies or uploads anything. It reports what it can
|
|
7
|
+
verify, what it only observed, and what it did not check.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```powershell
|
|
12
|
+
pip install modmanifest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Python 3.11+. From source: `pip install .`
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
modmanifest inspect PATH [--json]
|
|
21
|
+
modmanifest validate PATH --game sims4|inzoi [--json]
|
|
22
|
+
modmanifest audit-ai PATH [--json]
|
|
23
|
+
modmanifest --version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`PATH` is a folder, `.zip`, `.ts4script` or `.package`.
|
|
27
|
+
|
|
28
|
+
| Command | Runs |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `inspect` | archive safety, game recognition and validation, AI metadata audit |
|
|
31
|
+
| `validate` | archive safety and the selected game's validation |
|
|
32
|
+
| `audit-ai` | archive safety and the AI metadata audit |
|
|
33
|
+
|
|
34
|
+
Options: `--json`, `--verbose`, `--timeout`, `--max-entries`, `--max-total-mb`, `--max-entry-mb`.
|
|
35
|
+
|
|
36
|
+
### Statuses
|
|
37
|
+
|
|
38
|
+
`PASS`, `WARNING`, `ERROR`, `UNSUPPORTED`, `NOT CHECKED` (`NOT_CHECKED` in JSON).
|
|
39
|
+
Every finding carries a stable code (`MM-ARC-*`, `MM-SCN-*`, `MM-S4-*`, `MM-INZ-*`) and a
|
|
40
|
+
basis: `format-rule`, `observed-convention`, `safety-limit` or `scan-coverage`.
|
|
41
|
+
|
|
42
|
+
### Exit codes
|
|
43
|
+
|
|
44
|
+
| Code | Meaning |
|
|
45
|
+
|---|---|
|
|
46
|
+
| 0 | scan complete, no `ERROR` findings |
|
|
47
|
+
| 1 | one or more `ERROR` findings |
|
|
48
|
+
| 2 | invalid arguments or missing path |
|
|
49
|
+
| 3 | incomplete scan (limit, timeout, read failure), or `validate` recognized nothing |
|
|
50
|
+
|
|
51
|
+
AI indicators never change the exit code.
|
|
52
|
+
|
|
53
|
+
## Example
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
$ modmanifest validate SampleMod --game inzoi
|
|
57
|
+
ModManifest validate SampleMod
|
|
58
|
+
target: folder scan: complete
|
|
59
|
+
game selected: inzoi recognized: inzoi
|
|
60
|
+
Status Code Finding
|
|
61
|
+
NOT_CHECKED MM-INZ-015 no official inZOI package specification was used; ...
|
|
62
|
+
NOT_CHECKED MM-INZ-013 declared dependencies are reported as written; ...
|
|
63
|
+
NOT_CHECKED MM-INZ-017 game version metadata is reported as written; ...
|
|
64
|
+
NOT_CHECKED MM-INZ-009 checksum digests were not verified: the algorithm ...
|
|
65
|
+
PASS 2 WARNING 0 ERROR 0 UNSUPPORTED 0 NOT_CHECKED 5
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Validation coverage
|
|
69
|
+
|
|
70
|
+
### The Sims 4
|
|
71
|
+
|
|
72
|
+
- `.package` (DBPF 2.1): signature, header, index bounds and size, resource ranges,
|
|
73
|
+
duplicate keys. Resource contents are **not** decoded.
|
|
74
|
+
- `.ts4script`: opened as a ZIP, `.py`/`.pyc` entries listed, compiled-code header checked.
|
|
75
|
+
Script contents are **not** analyzed or executed.
|
|
76
|
+
- No standard Sims 4 manifest is assumed, so dependencies and versions are not extracted.
|
|
77
|
+
|
|
78
|
+
### inZOI
|
|
79
|
+
|
|
80
|
+
A root `mod_manifest.json` marks a **candidate** mod, not a valid package. There is no
|
|
81
|
+
official specification behind these checks.
|
|
82
|
+
|
|
83
|
+
- JSON syntax and field types, compared with sample packages. Observed fields are never required.
|
|
84
|
+
- Identity, author, version, description, declared dependencies and game-version metadata are
|
|
85
|
+
reported as written. Dependencies and compatibility are **not** verified.
|
|
86
|
+
- `ProjectName` versus folder name is informational.
|
|
87
|
+
- `FileChecksums`: shape, path safety and listed-file existence are checked. Digests are
|
|
88
|
+
**NOT CHECKED**: in sample packages the algorithm and path semantics could not be
|
|
89
|
+
established, and none is guessed. A matching digest would show consistency with the
|
|
90
|
+
manifest only, not that a mod is safe or authentic.
|
|
91
|
+
- `.pak`, `.ucas`, `.utoc`: recognized as Unreal content containers and inventoried;
|
|
92
|
+
internals are not inspected. A lone `.ucas` or `.utoc` is reported as a warning.
|
|
93
|
+
- Unrelated folders (mod-manager state, hosted content) are unrecognized, not invalid.
|
|
94
|
+
Point ModManifest at one mod folder at a time.
|
|
95
|
+
|
|
96
|
+
### Archive and folder safety (all targets)
|
|
97
|
+
|
|
98
|
+
Traversal and absolute paths, drive letters and alternate streams, backslashes, duplicates,
|
|
99
|
+
case collisions, symlinks and special files, entry count, total and per-entry size,
|
|
100
|
+
compression ratio, signature mismatch, encrypted entries (`UNSUPPORTED`), and nested
|
|
101
|
+
archives (listed, not opened). Limits are configurable.
|
|
102
|
+
|
|
103
|
+
## AI metadata audit
|
|
104
|
+
|
|
105
|
+
Reports indicators found in metadata, in three categories: explicit declaration, tool
|
|
106
|
+
reference, ambiguous indicator. Confidence refers to the interpretation of the metadata
|
|
107
|
+
only.
|
|
108
|
+
|
|
109
|
+
Supported sources: JSON, TOML, YAML and XML manifests; text and Markdown notes; PNG text
|
|
110
|
+
chunks; XMP; EXIF `Software`/`ImageDescription`. C2PA is **detection only**: possible
|
|
111
|
+
structures are reported, never parsed or signature-validated.
|
|
112
|
+
|
|
113
|
+
When nothing is found, the result is:
|
|
114
|
+
|
|
115
|
+
> No supported AI metadata indicators found.
|
|
116
|
+
|
|
117
|
+
Metadata may be absent, incomplete or removed. That result is not evidence about how a
|
|
118
|
+
package was made. Prompt text is never copied into reports. Name tables used for matching
|
|
119
|
+
live in `src/modmanifest/metadata/ai_signatures.py` and are detection data only.
|
|
120
|
+
|
|
121
|
+
## JSON output
|
|
122
|
+
|
|
123
|
+
Stable schema, `schema_version: 1`. Top-level keys: `tool`, `version`, `command`, `target`,
|
|
124
|
+
`game`, `scan`, `recognized_files`, `capabilities_exercised`, `package`, `findings`,
|
|
125
|
+
`summary`, `unsupported`, `inspection_errors`, `ai_metadata`. The target is a base name,
|
|
126
|
+
never an absolute path. Untrusted text has control and bidirectional characters escaped.
|
|
127
|
+
|
|
128
|
+
## Limitations
|
|
129
|
+
|
|
130
|
+
- Format checks reflect observed samples and public format knowledge, not vendor specs.
|
|
131
|
+
- Only listed metadata containers are audited; scripts and binary game data are not.
|
|
132
|
+
- 7z, RAR and other archive formats are listed, not opened.
|
|
133
|
+
- Package validity here does not mean a mod is safe, compatible or working in game.
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```powershell
|
|
138
|
+
pip install -e ".[dev]"
|
|
139
|
+
ruff check . ; ruff format --check .
|
|
140
|
+
pytest
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT. Copyright (c) CyberStudios.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Use GitHub private vulnerability reporting on this repository (Security tab, "Report a
|
|
6
|
+
vulnerability"). Do not open a public issue for security problems.
|
|
7
|
+
|
|
8
|
+
## Scope
|
|
9
|
+
|
|
10
|
+
ModManifest parses untrusted archives and files. In scope: crashes, resource exhaustion,
|
|
11
|
+
path handling, or any behavior that writes, executes or transmits data. Out of scope:
|
|
12
|
+
whether a mod is safe to install; ModManifest makes no such claim.
|
|
13
|
+
|
|
14
|
+
## Supported versions
|
|
15
|
+
|
|
16
|
+
The latest release.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modmanifest"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Read-only inspection, validation and metadata auditing for game mod packages (The Sims 4, inZOI)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "CyberStudios" }]
|
|
14
|
+
keywords = ["mods", "sims4", "inzoi", "audit", "metadata", "cli"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"typer>=0.12",
|
|
29
|
+
"rich>=13.7",
|
|
30
|
+
"PyYAML>=6.0",
|
|
31
|
+
"defusedxml>=0.7.1",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["pytest>=8.0", "ruff>=0.6"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
modmanifest = "modmanifest.cli:main"
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/CyberStudiosTM/ModManifest"
|
|
42
|
+
Issues = "https://github.com/CyberStudiosTM/ModManifest/issues"
|
|
43
|
+
Changelog = "https://github.com/CyberStudiosTM/ModManifest/blob/main/CHANGELOG.md"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.version]
|
|
46
|
+
path = "src/modmanifest/__init__.py"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/modmanifest"]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.sdist]
|
|
52
|
+
include = ["src", "tests", "README.md", "LICENSE", "CHANGELOG.md", "SECURITY.md", "CONTRIBUTING.md", "pyproject.toml"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
addopts = "-q"
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 100
|
|
60
|
+
target-version = "py311"
|
|
61
|
+
src = ["src", "tests"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "ANN", "PT"]
|
|
65
|
+
ignore = ["ANN401"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.per-file-ignores]
|
|
68
|
+
"tests/**" = ["ANN", "PT018", "E501"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package marker."""
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Configurable scan limits and the scan deadline."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
MB = 1024 * 1024
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class ScanLimits:
|
|
13
|
+
max_entries: int = 20_000
|
|
14
|
+
max_archive_bytes: int = 1024 * MB
|
|
15
|
+
max_total_bytes: int = 500 * MB
|
|
16
|
+
max_entry_bytes: int = 50 * MB
|
|
17
|
+
max_ratio: float = 100.0
|
|
18
|
+
max_metadata_bytes: int = 1 * MB
|
|
19
|
+
max_image_bytes: int = 20 * MB
|
|
20
|
+
timeout_s: float = 30.0
|
|
21
|
+
# Ratio warnings are skipped for tiny entries, which compress extremely well legitimately.
|
|
22
|
+
ratio_min_bytes: int = 64 * 1024
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ScanTimeout(Exception):
|
|
26
|
+
"""Raised when the scan deadline passes."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Deadline:
|
|
30
|
+
def __init__(self, seconds: float) -> None:
|
|
31
|
+
self._end = time.monotonic() + seconds
|
|
32
|
+
self.seconds = seconds
|
|
33
|
+
|
|
34
|
+
def check(self) -> None:
|
|
35
|
+
if time.monotonic() > self._end:
|
|
36
|
+
raise ScanTimeout(f"scan exceeded the {self.seconds:g}s time limit")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Structural file-signature checks for extensions where a signature is well defined."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from modmanifest.context import ScanContext
|
|
6
|
+
from modmanifest.models import Finding, Status
|
|
7
|
+
|
|
8
|
+
_ZIP = (b"PK\x03\x04", b"PK\x05\x06")
|
|
9
|
+
SIGNATURES: dict[str, tuple[bytes, ...]] = {
|
|
10
|
+
".png": (b"\x89PNG\r\n\x1a\n",),
|
|
11
|
+
".jpg": (b"\xff\xd8\xff",),
|
|
12
|
+
".jpeg": (b"\xff\xd8\xff",),
|
|
13
|
+
".zip": _ZIP,
|
|
14
|
+
".ts4script": _ZIP,
|
|
15
|
+
}
|
|
16
|
+
# .package signatures are verified by the Sims 4 DBPF parser, not here.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def check_signatures(ctx: ScanContext) -> None:
|
|
20
|
+
checked = 0
|
|
21
|
+
for entry in ctx.source.files():
|
|
22
|
+
ext = "." + entry.path.rsplit(".", 1)[-1].lower() if "." in entry.path else ""
|
|
23
|
+
sigs = SIGNATURES.get(ext)
|
|
24
|
+
is_webp = ext == ".webp"
|
|
25
|
+
if sigs is None and not is_webp:
|
|
26
|
+
continue
|
|
27
|
+
ctx.deadline.check()
|
|
28
|
+
head = ctx.head(entry.path, 12)
|
|
29
|
+
if head is None:
|
|
30
|
+
continue
|
|
31
|
+
checked += 1
|
|
32
|
+
ok = (
|
|
33
|
+
head[:4] == b"RIFF" and head[8:12] == b"WEBP"
|
|
34
|
+
if is_webp
|
|
35
|
+
else any(head.startswith(s) for s in sigs or ())
|
|
36
|
+
)
|
|
37
|
+
if not ok:
|
|
38
|
+
ctx.report.add(
|
|
39
|
+
Finding(
|
|
40
|
+
"MM-ARC-013",
|
|
41
|
+
Status.WARNING,
|
|
42
|
+
f"extension '{ext}' does not match the file signature",
|
|
43
|
+
source=entry.path,
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
if checked:
|
|
47
|
+
ctx.report.capability("archive.signature_check")
|