jame 1.0.dev0__tar.gz → 1.0.dev3__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.
Files changed (37) hide show
  1. {jame-1.0.dev0 → jame-1.0.dev3}/PKG-INFO +40 -26
  2. {jame-1.0.dev0 → jame-1.0.dev3}/README.md +37 -25
  3. jame-1.0.dev3/jame/cli/jame_cli.py +210 -0
  4. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/PKG-INFO +40 -26
  5. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/requires.txt +2 -0
  6. {jame-1.0.dev0 → jame-1.0.dev3}/pyproject.toml +3 -1
  7. jame-1.0.dev3/tests/test_cli.py +116 -0
  8. jame-1.0.dev0/jame/cli/jame_cli.py +0 -151
  9. jame-1.0.dev0/tests/test_cli.py +0 -57
  10. {jame-1.0.dev0 → jame-1.0.dev3}/LICENSE +0 -0
  11. {jame-1.0.dev0 → jame-1.0.dev3}/jame/__init__.py +0 -0
  12. {jame-1.0.dev0 → jame-1.0.dev3}/jame/__main__.py +0 -0
  13. {jame-1.0.dev0 → jame-1.0.dev3}/jame/cli/__init__.py +0 -0
  14. {jame-1.0.dev0 → jame-1.0.dev3}/jame/core/__init__.py +0 -0
  15. {jame-1.0.dev0 → jame-1.0.dev3}/jame/core/filetype.py +0 -0
  16. {jame-1.0.dev0 → jame-1.0.dev3}/jame/core/jame.py +0 -0
  17. {jame-1.0.dev0 → jame-1.0.dev3}/jame/core/patterns.py +0 -0
  18. {jame-1.0.dev0 → jame-1.0.dev3}/jame/core/utils.py +0 -0
  19. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/__init__.py +0 -0
  20. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/archive.py +0 -0
  21. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/binary.py +0 -0
  22. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/crypto.py +0 -0
  23. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/forensics.py +0 -0
  24. {jame-1.0.dev0 → jame-1.0.dev3}/jame/modules/stego.py +0 -0
  25. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/__init__.py +0 -0
  26. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/binary_result.py +0 -0
  27. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/crypto_result.py +0 -0
  28. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/forensics_result.py +0 -0
  29. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/scan_result.py +0 -0
  30. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/solve_result.py +0 -0
  31. {jame-1.0.dev0 → jame-1.0.dev3}/jame/results/stego_result.py +0 -0
  32. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/SOURCES.txt +0 -0
  33. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/dependency_links.txt +0 -0
  34. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/entry_points.txt +0 -0
  35. {jame-1.0.dev0 → jame-1.0.dev3}/jame.egg-info/top_level.txt +0 -0
  36. {jame-1.0.dev0 → jame-1.0.dev3}/setup.cfg +0 -0
  37. {jame-1.0.dev0 → jame-1.0.dev3}/tests/test_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jame
3
- Version: 1.0.dev0
3
+ Version: 1.0.dev3
4
4
  Summary: Just Another Method of Exploitation
5
5
  Author: JAMe Contributors
6
6
  License: MIT
@@ -19,6 +19,8 @@ Classifier: Development Status :: 4 - Beta
19
19
  Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: c2e
23
+ Requires-Dist: mapres
22
24
  Requires-Dist: PyYAML>=6.0
23
25
  Dynamic: license-file
24
26
 
@@ -40,31 +42,36 @@ JAMe is a lightweight forensic and triage toolkit for scanning files, extracting
40
42
 
41
43
  ## Installation
42
44
 
43
- JAMe requires Python 3.10 or newer. From the repository root, this one-line
44
- installer creates the virtual environment, installs JAMe, links the command
45
- into the absolute path `$HOME/.local/bin`, and adds that directory to the
46
- current shell and future Bash login/interactive shells:
45
+ JAMe requires Python 3.10 or newer. Install the latest published package and
46
+ register `jame` on your user PATH with:
47
47
 
48
48
  ```bash
49
- python3 -m venv .venv && .venv/bin/pip install jame && mkdir -p "$HOME/.local/bin" && if [[ -e "$HOME/.local/bin/jame" && ! "$HOME/.local/bin/jame" -ef "$(pwd -P)/.venv/bin/jame" ]]; then printf '%s\n' 'Refusing to replace existing command at ~/.local/bin/jame' >&2; exit 1; fi && ln -sfn "$(pwd -P)/.venv/bin/jame" "$HOME/.local/bin/jame" && for rc in "$HOME/.profile" "$HOME/.bashrc"; do grep -qxF 'export PATH="$HOME/.local/bin:$PATH"' "$rc" 2>/dev/null || printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' >> "$rc"; done && export PATH="$HOME/.local/bin:$PATH" && jame --help
49
+ curl -fsSL https://raw.githubusercontent.com/HungerNet/JAMe/main/install.sh | bash
50
50
  ```
51
51
 
52
+ The installer creates a dedicated environment under `$HOME/.local/share/jame`,
53
+ installs the MapRes pre-release API, `jame`, and C2E, and links the command to
54
+ `$HOME/.local/bin/jame`. It adds that directory to Bash or Zsh startup files;
55
+ open a new shell if it was not already on your current PATH.
56
+
57
+ Set `JAME_PYTHON` to select a specific Python 3.10+ executable. Set
58
+ `JAME_INSTALL_DIR` or `JAME_BIN_DIR` to change the absolute install or bin path.
59
+
52
60
  ## CLI
53
61
 
54
- From a checkout, the Bash launcher can also be run directly; it uses the active
55
- virtual environment, the checkout's `.venv`, or `python3.10`, in that order:
62
+ After installation, run the `jame` command from any directory:
56
63
 
57
64
  ```bash
58
- ./jame.sh --help
59
- ./jame.sh tests/files/solution.txt
65
+ jame --help
66
+ jame tests/files/solution.txt
60
67
  ```
61
68
 
62
- The default command finds a flag and extracts supported archives. The original
63
- path-only form remains supported:
69
+ The default command finds a flag and extracts supported archives. The explicit
70
+ `solve` form is also available:
64
71
 
65
72
  ```bash
66
- ./jame.sh tests/files/solution.txt
67
- ./jame.sh solve tests/files/solution.txt --format 'flag{*}'
73
+ jame tests/files/solution.txt
74
+ jame solve tests/files/solution.txt --format 'flag{*}'
68
75
  ```
69
76
 
70
77
  Use `*` for any-length text and `*N` for exactly `N` characters. Repeating
@@ -74,21 +81,28 @@ to require five characters, not `hcr{*****}`.
74
81
  Other available methods:
75
82
 
76
83
  ```bash
77
- ./jame.sh find-flag tests/files/challenge.txt --format 'hcr{*4}'
78
- ./jame.sh scan tests/files/scan.txt
79
- ./jame.sh crypto tests/files/strings.txt --verbose
80
- ./jame.sh binary tests/files/strings.txt
81
- ./jame.sh stego tests/files/strings.txt
82
- ./jame.sh forensics tests/files/strings.txt
83
- ./jame.sh archive path/to/archive.zip --max-depth 2
84
- ./jame.sh file-type tests/files/strings.txt
85
- ./jame.sh strings tests/files/strings.txt
86
- ./jame.sh binwalk path/to/file.bin
87
- ./jame.sh caesar 'Khoor' --shift 3
84
+ jame update
85
+ jame update --pre
86
+ jame find-flag tests/files/challenge.txt --format 'hcr{*4}'
87
+ jame scan tests/files/scan.txt
88
+ jame crypto tests/files/strings.txt --verbose
89
+ jame binary tests/files/strings.txt
90
+ jame stego tests/files/strings.txt
91
+ jame forensics tests/files/strings.txt
92
+ jame archive path/to/archive.zip --max-depth 2
93
+ jame file-type tests/files/strings.txt
94
+ jame strings tests/files/strings.txt
95
+ jame binwalk path/to/file.bin
96
+ jame caesar 'Khoor'
97
+ jame caesar 'Khoor' --verbose
98
+ jame caesar 'Khoor' --shift 3
88
99
  ```
89
100
 
90
101
  `--verbose` prints the structured result, including analyzer details and raw
91
- output where available. `binwalk` requires the external `binwalk` utility.
102
+ output where available. Caesar brute force prints only its highest-ranked
103
+ decoded candidate by default; `--verbose` shows the ranked candidates.
104
+ `jame update` upgrades JAMe and C2E; `jame update --pre` also upgrades MapRes
105
+ using prerelease versions. `binwalk` requires the external `binwalk` utility.
92
106
 
93
107
  ## Quickstart
94
108
 
@@ -16,31 +16,36 @@ JAMe is a lightweight forensic and triage toolkit for scanning files, extracting
16
16
 
17
17
  ## Installation
18
18
 
19
- JAMe requires Python 3.10 or newer. From the repository root, this one-line
20
- installer creates the virtual environment, installs JAMe, links the command
21
- into the absolute path `$HOME/.local/bin`, and adds that directory to the
22
- current shell and future Bash login/interactive shells:
19
+ JAMe requires Python 3.10 or newer. Install the latest published package and
20
+ register `jame` on your user PATH with:
23
21
 
24
22
  ```bash
25
- python3 -m venv .venv && .venv/bin/pip install jame && mkdir -p "$HOME/.local/bin" && if [[ -e "$HOME/.local/bin/jame" && ! "$HOME/.local/bin/jame" -ef "$(pwd -P)/.venv/bin/jame" ]]; then printf '%s\n' 'Refusing to replace existing command at ~/.local/bin/jame' >&2; exit 1; fi && ln -sfn "$(pwd -P)/.venv/bin/jame" "$HOME/.local/bin/jame" && for rc in "$HOME/.profile" "$HOME/.bashrc"; do grep -qxF 'export PATH="$HOME/.local/bin:$PATH"' "$rc" 2>/dev/null || printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' >> "$rc"; done && export PATH="$HOME/.local/bin:$PATH" && jame --help
23
+ curl -fsSL https://raw.githubusercontent.com/HungerNet/JAMe/main/install.sh | bash
26
24
  ```
27
25
 
26
+ The installer creates a dedicated environment under `$HOME/.local/share/jame`,
27
+ installs the MapRes pre-release API, `jame`, and C2E, and links the command to
28
+ `$HOME/.local/bin/jame`. It adds that directory to Bash or Zsh startup files;
29
+ open a new shell if it was not already on your current PATH.
30
+
31
+ Set `JAME_PYTHON` to select a specific Python 3.10+ executable. Set
32
+ `JAME_INSTALL_DIR` or `JAME_BIN_DIR` to change the absolute install or bin path.
33
+
28
34
  ## CLI
29
35
 
30
- From a checkout, the Bash launcher can also be run directly; it uses the active
31
- virtual environment, the checkout's `.venv`, or `python3.10`, in that order:
36
+ After installation, run the `jame` command from any directory:
32
37
 
33
38
  ```bash
34
- ./jame.sh --help
35
- ./jame.sh tests/files/solution.txt
39
+ jame --help
40
+ jame tests/files/solution.txt
36
41
  ```
37
42
 
38
- The default command finds a flag and extracts supported archives. The original
39
- path-only form remains supported:
43
+ The default command finds a flag and extracts supported archives. The explicit
44
+ `solve` form is also available:
40
45
 
41
46
  ```bash
42
- ./jame.sh tests/files/solution.txt
43
- ./jame.sh solve tests/files/solution.txt --format 'flag{*}'
47
+ jame tests/files/solution.txt
48
+ jame solve tests/files/solution.txt --format 'flag{*}'
44
49
  ```
45
50
 
46
51
  Use `*` for any-length text and `*N` for exactly `N` characters. Repeating
@@ -50,21 +55,28 @@ to require five characters, not `hcr{*****}`.
50
55
  Other available methods:
51
56
 
52
57
  ```bash
53
- ./jame.sh find-flag tests/files/challenge.txt --format 'hcr{*4}'
54
- ./jame.sh scan tests/files/scan.txt
55
- ./jame.sh crypto tests/files/strings.txt --verbose
56
- ./jame.sh binary tests/files/strings.txt
57
- ./jame.sh stego tests/files/strings.txt
58
- ./jame.sh forensics tests/files/strings.txt
59
- ./jame.sh archive path/to/archive.zip --max-depth 2
60
- ./jame.sh file-type tests/files/strings.txt
61
- ./jame.sh strings tests/files/strings.txt
62
- ./jame.sh binwalk path/to/file.bin
63
- ./jame.sh caesar 'Khoor' --shift 3
58
+ jame update
59
+ jame update --pre
60
+ jame find-flag tests/files/challenge.txt --format 'hcr{*4}'
61
+ jame scan tests/files/scan.txt
62
+ jame crypto tests/files/strings.txt --verbose
63
+ jame binary tests/files/strings.txt
64
+ jame stego tests/files/strings.txt
65
+ jame forensics tests/files/strings.txt
66
+ jame archive path/to/archive.zip --max-depth 2
67
+ jame file-type tests/files/strings.txt
68
+ jame strings tests/files/strings.txt
69
+ jame binwalk path/to/file.bin
70
+ jame caesar 'Khoor'
71
+ jame caesar 'Khoor' --verbose
72
+ jame caesar 'Khoor' --shift 3
64
73
  ```
65
74
 
66
75
  `--verbose` prints the structured result, including analyzer details and raw
67
- output where available. `binwalk` requires the external `binwalk` utility.
76
+ output where available. Caesar brute force prints only its highest-ranked
77
+ decoded candidate by default; `--verbose` shows the ranked candidates.
78
+ `jame update` upgrades JAMe and C2E; `jame update --pre` also upgrades MapRes
79
+ using prerelease versions. `binwalk` requires the external `binwalk` utility.
68
80
 
69
81
  ## Quickstart
70
82
 
@@ -0,0 +1,210 @@
1
+ '''Command-line entry point for JAMe.'''
2
+
3
+ import json
4
+ import os
5
+ import subprocess
6
+ import sys
7
+
8
+ from c2e import LiveCLI, command
9
+ from mapres import ascii_colors, rprint, res, setGlobalMaps
10
+
11
+ from jame import Jame
12
+
13
+ setGlobalMaps(ascii_colors)
14
+ _COLOR_ENABLED = sys.stdout.isatty() and 'NO_COLOR' not in os.environ
15
+
16
+
17
+ def _paint(text: object, color: str, *, bold: bool = False) -> str:
18
+ if not _COLOR_ENABLED:
19
+ return str(text)
20
+ style = res(f'<{color}>')
21
+ if bold:
22
+ style = res('<bold>') + style
23
+ return f'{style}{text}{res("<reset>")}'
24
+
25
+
26
+ def _print_result(cli: LiveCLI, result: object, detailed: bool = False) -> None:
27
+ if detailed:
28
+ to_dict = getattr(result, 'to_dict', None)
29
+ output = to_dict() if callable(to_dict) else result
30
+ cli.safePrint(json.dumps(output, indent=2, default=str))
31
+ elif hasattr(result, 'summary'):
32
+ if hasattr(result, 'flag'):
33
+ color = 'green' if result.flag else 'yellow'
34
+ else:
35
+ color = 'green' if getattr(result, 'confidence', 0.0) >= 0.7 else 'aqua'
36
+ cli.safePrint(_paint(result.summary, color))
37
+ if getattr(result, 'flag', None):
38
+ cli.safePrint(_paint(result.flag, 'green', bold=True))
39
+ elif result is None:
40
+ cli.safePrint(_paint('No flag found.', 'yellow'))
41
+ elif isinstance(result, list):
42
+ output = '\n'.join(str(item) for item in result) if result else 'No results.'
43
+ cli.safePrint(_paint(output, 'blue'))
44
+ elif isinstance(result, (dict, tuple)):
45
+ cli.safePrint(_paint(json.dumps(result, indent=2, default=str), 'blue'))
46
+ else:
47
+ cli.safePrint(_paint(result, 'aqua'))
48
+
49
+
50
+ @command('solve')
51
+ def solve(cli: LiveCLI, path: str) -> None:
52
+ '''Find a flag and extract supported archives.'''
53
+ engine = Jame(flag_patterns=[format()], max_depth=max_depth(), verbose=verbose())
54
+ result = engine.autoSolve(
55
+ path,
56
+ format=format(),
57
+ maxDepth=max_depth(),
58
+ verbose=verbose(),
59
+ )
60
+ _print_result(cli, result, verbose())
61
+
62
+
63
+ @solve.param('format', type=str, default='flag{*}')
64
+ def _solve_format(value):
65
+ '''Flag pattern; '*' matches any length and '*N' matches exactly N characters.'''
66
+ return value
67
+
68
+
69
+ @solve.param('max-depth', type=int, default=3)
70
+ def _solve_max_depth(value):
71
+ '''Maximum recursive archive extraction depth.'''
72
+ return value
73
+
74
+
75
+ @solve.flag('verbose')
76
+ def _solve_verbose(value):
77
+ '''Print structured result details.'''
78
+ return value
79
+
80
+
81
+ @command('find-flag')
82
+ def find_flag(cli: LiveCLI, path: str) -> None:
83
+ '''Find a flag matching a wildcard pattern.'''
84
+ result = Jame().findFlag(path, format())
85
+ _print_result(cli, result, verbose())
86
+
87
+
88
+ @find_flag.param('format', type=str, default='flag{*}')
89
+ def _find_flag_format(value):
90
+ '''Flag pattern; '*' matches any length and '*N' matches exactly N characters.'''
91
+ return value
92
+
93
+
94
+ @find_flag.flag('verbose')
95
+ def _find_flag_verbose(value):
96
+ '''Print structured result details.'''
97
+ return value
98
+
99
+
100
+ def _register_path_command(name: str, method_name: str, description: str) -> None:
101
+ @command(name)
102
+ def handler(cli: LiveCLI, path: str) -> None:
103
+ result = getattr(Jame(), method_name)(path)
104
+ _print_result(cli, result, verbose())
105
+
106
+ handler.func.__doc__ = description
107
+ handler.desc = description
108
+ def verbose_flag(value):
109
+ '''Print structured result details.'''
110
+ return value
111
+
112
+ handler.flag('verbose')(verbose_flag)
113
+
114
+
115
+ _register_path_command('scan', 'scanFile', 'Run all available analyzers.')
116
+ _register_path_command('crypto', 'cryptoAnalyze', 'Analyze crypto indicators.')
117
+ _register_path_command('binary', 'binaryAnalyze', 'Analyze binary indicators.')
118
+ _register_path_command('stego', 'stegoAnalyze', 'Check for steganography indicators.')
119
+ _register_path_command('forensics', 'forensicsAnalyze', 'Run forensic checks.')
120
+ _register_path_command('file-type', 'getFileType', 'Detect the file type.')
121
+ _register_path_command('strings', 'runStrings', 'Extract printable strings.')
122
+ _register_path_command('binwalk', 'runBinwalk', 'Run binwalk when installed.')
123
+
124
+
125
+ @command('archive')
126
+ def archive(cli: LiveCLI, path: str) -> None:
127
+ '''Extract supported archives.'''
128
+ engine = Jame(max_depth=max_depth(), verbose=verbose())
129
+ _print_result(cli, engine.archiveExtract(path), verbose())
130
+
131
+
132
+ @archive.param('max-depth', type=int, default=3)
133
+ def _archive_max_depth(value):
134
+ '''Maximum recursive archive extraction depth.'''
135
+ return value
136
+
137
+
138
+ @archive.flag('verbose')
139
+ def _archive_verbose(value):
140
+ '''Print structured result details.'''
141
+ return value
142
+
143
+
144
+ @command('caesar')
145
+ def caesar(cli: LiveCLI, text: str) -> None:
146
+ '''Decode Caesar-shifted text.'''
147
+ shift_value = shift()
148
+ if shift_value != 'brute':
149
+ try:
150
+ shift_value = int(shift_value)
151
+ except ValueError:
152
+ cli.safePrint("Error: --shift must be an integer or 'brute'")
153
+ raise SystemExit(2) from None
154
+ result = Jame().decodeCaesar(text, shift_value)
155
+ if verbose():
156
+ _print_result(cli, result, detailed=True)
157
+ elif shift_value == 'brute':
158
+ best = result.get('best')
159
+ if best:
160
+ cli.safePrint(_paint(best['decoded'], 'green', bold=True))
161
+ else:
162
+ cli.safePrint(_paint('No Caesar candidate found.', 'yellow'))
163
+ else:
164
+ cli.safePrint(_paint(result.get('decoded', result.get('error', '')), 'green'))
165
+
166
+
167
+ @caesar.param('shift', type=str, default='brute')
168
+ def _caesar_shift(value):
169
+ '''Integer shift or 'brute' to check every Caesar shift.'''
170
+ return value
171
+
172
+
173
+ @caesar.flag('verbose')
174
+ def _caesar_verbose(value):
175
+ '''Print structured result details.'''
176
+ return value
177
+
178
+
179
+ @command('update')
180
+ def update(cli: LiveCLI) -> None:
181
+ '''Upgrade JAMe and C2E, optionally including the MapRes prerelease.'''
182
+ rprint(_paint('Updating JAMe...', 'aqua'))
183
+ command = [sys.executable, '-m', 'pip', 'install', '--upgrade']
184
+ if pre():
185
+ command.append('--pre')
186
+ command.extend(['c2e', 'jame'])
187
+ if pre():
188
+ command.append('mapres')
189
+ result = subprocess.run(command, check=False)
190
+ if result.returncode:
191
+ raise SystemExit(result.returncode)
192
+
193
+
194
+ @update.flag('pre')
195
+ def _update_pre(value):
196
+ '''Include pre-release versions when upgrading.'''
197
+ return value
198
+
199
+
200
+ def main() -> None:
201
+ '''Parse command-line options and dispatch them through C2E.'''
202
+ LiveCLI().run_argv(
203
+ prog='jame',
204
+ description='Just Another Method of Exploitation',
205
+ default_command='solve',
206
+ )
207
+
208
+
209
+ if __name__ == '__main__':
210
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jame
3
- Version: 1.0.dev0
3
+ Version: 1.0.dev3
4
4
  Summary: Just Another Method of Exploitation
5
5
  Author: JAMe Contributors
6
6
  License: MIT
@@ -19,6 +19,8 @@ Classifier: Development Status :: 4 - Beta
19
19
  Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: c2e
23
+ Requires-Dist: mapres
22
24
  Requires-Dist: PyYAML>=6.0
23
25
  Dynamic: license-file
24
26
 
@@ -40,31 +42,36 @@ JAMe is a lightweight forensic and triage toolkit for scanning files, extracting
40
42
 
41
43
  ## Installation
42
44
 
43
- JAMe requires Python 3.10 or newer. From the repository root, this one-line
44
- installer creates the virtual environment, installs JAMe, links the command
45
- into the absolute path `$HOME/.local/bin`, and adds that directory to the
46
- current shell and future Bash login/interactive shells:
45
+ JAMe requires Python 3.10 or newer. Install the latest published package and
46
+ register `jame` on your user PATH with:
47
47
 
48
48
  ```bash
49
- python3 -m venv .venv && .venv/bin/pip install jame && mkdir -p "$HOME/.local/bin" && if [[ -e "$HOME/.local/bin/jame" && ! "$HOME/.local/bin/jame" -ef "$(pwd -P)/.venv/bin/jame" ]]; then printf '%s\n' 'Refusing to replace existing command at ~/.local/bin/jame' >&2; exit 1; fi && ln -sfn "$(pwd -P)/.venv/bin/jame" "$HOME/.local/bin/jame" && for rc in "$HOME/.profile" "$HOME/.bashrc"; do grep -qxF 'export PATH="$HOME/.local/bin:$PATH"' "$rc" 2>/dev/null || printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' >> "$rc"; done && export PATH="$HOME/.local/bin:$PATH" && jame --help
49
+ curl -fsSL https://raw.githubusercontent.com/HungerNet/JAMe/main/install.sh | bash
50
50
  ```
51
51
 
52
+ The installer creates a dedicated environment under `$HOME/.local/share/jame`,
53
+ installs the MapRes pre-release API, `jame`, and C2E, and links the command to
54
+ `$HOME/.local/bin/jame`. It adds that directory to Bash or Zsh startup files;
55
+ open a new shell if it was not already on your current PATH.
56
+
57
+ Set `JAME_PYTHON` to select a specific Python 3.10+ executable. Set
58
+ `JAME_INSTALL_DIR` or `JAME_BIN_DIR` to change the absolute install or bin path.
59
+
52
60
  ## CLI
53
61
 
54
- From a checkout, the Bash launcher can also be run directly; it uses the active
55
- virtual environment, the checkout's `.venv`, or `python3.10`, in that order:
62
+ After installation, run the `jame` command from any directory:
56
63
 
57
64
  ```bash
58
- ./jame.sh --help
59
- ./jame.sh tests/files/solution.txt
65
+ jame --help
66
+ jame tests/files/solution.txt
60
67
  ```
61
68
 
62
- The default command finds a flag and extracts supported archives. The original
63
- path-only form remains supported:
69
+ The default command finds a flag and extracts supported archives. The explicit
70
+ `solve` form is also available:
64
71
 
65
72
  ```bash
66
- ./jame.sh tests/files/solution.txt
67
- ./jame.sh solve tests/files/solution.txt --format 'flag{*}'
73
+ jame tests/files/solution.txt
74
+ jame solve tests/files/solution.txt --format 'flag{*}'
68
75
  ```
69
76
 
70
77
  Use `*` for any-length text and `*N` for exactly `N` characters. Repeating
@@ -74,21 +81,28 @@ to require five characters, not `hcr{*****}`.
74
81
  Other available methods:
75
82
 
76
83
  ```bash
77
- ./jame.sh find-flag tests/files/challenge.txt --format 'hcr{*4}'
78
- ./jame.sh scan tests/files/scan.txt
79
- ./jame.sh crypto tests/files/strings.txt --verbose
80
- ./jame.sh binary tests/files/strings.txt
81
- ./jame.sh stego tests/files/strings.txt
82
- ./jame.sh forensics tests/files/strings.txt
83
- ./jame.sh archive path/to/archive.zip --max-depth 2
84
- ./jame.sh file-type tests/files/strings.txt
85
- ./jame.sh strings tests/files/strings.txt
86
- ./jame.sh binwalk path/to/file.bin
87
- ./jame.sh caesar 'Khoor' --shift 3
84
+ jame update
85
+ jame update --pre
86
+ jame find-flag tests/files/challenge.txt --format 'hcr{*4}'
87
+ jame scan tests/files/scan.txt
88
+ jame crypto tests/files/strings.txt --verbose
89
+ jame binary tests/files/strings.txt
90
+ jame stego tests/files/strings.txt
91
+ jame forensics tests/files/strings.txt
92
+ jame archive path/to/archive.zip --max-depth 2
93
+ jame file-type tests/files/strings.txt
94
+ jame strings tests/files/strings.txt
95
+ jame binwalk path/to/file.bin
96
+ jame caesar 'Khoor'
97
+ jame caesar 'Khoor' --verbose
98
+ jame caesar 'Khoor' --shift 3
88
99
  ```
89
100
 
90
101
  `--verbose` prints the structured result, including analyzer details and raw
91
- output where available. `binwalk` requires the external `binwalk` utility.
102
+ output where available. Caesar brute force prints only its highest-ranked
103
+ decoded candidate by default; `--verbose` shows the ranked candidates.
104
+ `jame update` upgrades JAMe and C2E; `jame update --pre` also upgrades MapRes
105
+ using prerelease versions. `binwalk` requires the external `binwalk` utility.
92
106
 
93
107
  ## Quickstart
94
108
 
@@ -1 +1,3 @@
1
+ c2e
2
+ mapres
1
3
  PyYAML>=6.0
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "jame"
10
- version = "1.0-dev"
10
+ version = "1.0-dev.3"
11
11
  description = "Just Another Method of Exploitation"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -24,6 +24,8 @@ keywords = [
24
24
  "jame",
25
25
  ]
26
26
  dependencies = [
27
+ "c2e",
28
+ "mapres",
27
29
  "PyYAML>=6.0",
28
30
  ]
29
31
  classifiers = [
@@ -0,0 +1,116 @@
1
+ '''Integration tests for the JAMe command-line interface.'''
2
+
3
+ import json
4
+ import sys
5
+ from types import SimpleNamespace
6
+
7
+ from mapres import ascii_colors
8
+
9
+ from jame.cli import jame_cli
10
+ from jame.cli.jame_cli import main
11
+
12
+
13
+ def run_cli(monkeypatch, capsys, *arguments: str) -> str:
14
+ monkeypatch.setattr(sys, 'argv', ['jame', *arguments])
15
+ monkeypatch.setattr(jame_cli, '_COLOR_ENABLED', False)
16
+ main()
17
+ return capsys.readouterr().out
18
+
19
+
20
+ def test_cli_uses_mapres_color_tokens(monkeypatch):
21
+ '''Render color tokens from MapRes's registered ASCII map.'''
22
+ monkeypatch.setattr(jame_cli, '_COLOR_ENABLED', True)
23
+
24
+ assert jame_cli._paint('candidate', 'orange') == f'{ascii_colors.orange}candidate{ascii_colors.reset}'
25
+
26
+
27
+ def test_cli_uses_numbered_wildcard_for_exact_length(monkeypatch, capsys):
28
+ '''Treat numbered wildcard length as exact and unnumbered stars as open-ended.'''
29
+ unbounded = run_cli(
30
+ monkeypatch,
31
+ capsys,
32
+ 'tests/files/challenge.txt',
33
+ '--format',
34
+ 'hcr{*****}',
35
+ )
36
+ assert 'hcr{abcd}' in unbounded
37
+
38
+ exact = run_cli(
39
+ monkeypatch,
40
+ capsys,
41
+ 'tests/files/challenge.txt',
42
+ '--format',
43
+ 'hcr{*5}',
44
+ )
45
+ assert 'No flag extracted.' in exact
46
+ assert 'hcr{abcd}' not in exact
47
+
48
+
49
+ def test_cli_verbose_prints_structured_analyzer_details(monkeypatch, capsys):
50
+ '''Expose structured result details with the verbose flag.'''
51
+ output = run_cli(
52
+ monkeypatch,
53
+ capsys,
54
+ 'crypto',
55
+ 'tests/files/strings.txt',
56
+ '--verbose',
57
+ )
58
+
59
+ result = json.loads(output)
60
+ assert result['file_path'] == 'tests/files/strings.txt'
61
+ assert 'findings' in result
62
+
63
+
64
+ def test_cli_supports_caesar_method(monkeypatch, capsys):
65
+ '''Return only the best Caesar decode by default.'''
66
+ output = run_cli(monkeypatch, capsys, 'caesar', 'Khoor')
67
+
68
+ assert output.strip() == 'Hello'
69
+
70
+
71
+ def test_cli_caesar_verbose_shows_ranked_candidates(monkeypatch, capsys):
72
+ '''Expose ranked Caesar candidates through verbose output.'''
73
+ output = run_cli(monkeypatch, capsys, 'caesar', 'Khoor', '--verbose')
74
+
75
+ result = json.loads(output)
76
+ assert result['best']['decoded'] == 'Hello'
77
+ assert len(result['results']) == 25
78
+
79
+
80
+ def test_cli_caesar_explicit_shift(monkeypatch, capsys):
81
+ '''Decode one Caesar shift without verbose metadata.'''
82
+ output = run_cli(monkeypatch, capsys, 'caesar', 'Khoor', '--shift', '3')
83
+
84
+ assert output.strip() == 'Hello'
85
+
86
+
87
+ def test_cli_update_pre_requests_prereleases(monkeypatch, capsys):
88
+ '''Pass --pre through to pip when updating JAMe and C2E.'''
89
+ calls = []
90
+
91
+ def fake_run(arguments, check):
92
+ calls.append((arguments, check))
93
+ return SimpleNamespace(returncode=0)
94
+
95
+ monkeypatch.setattr(jame_cli.subprocess, 'run', fake_run)
96
+ run_cli(monkeypatch, capsys, 'update', '--pre')
97
+
98
+ arguments, check = calls[0]
99
+ assert arguments[-5:] == ['--upgrade', '--pre', 'c2e', 'jame', 'mapres']
100
+ assert check is False
101
+
102
+
103
+ def test_cli_stable_update_leaves_mapres_prerelease_installed(monkeypatch, capsys):
104
+ '''Avoid downgrading the required MapRes API during a stable update.'''
105
+ calls = []
106
+
107
+ def fake_run(arguments, check):
108
+ calls.append(arguments)
109
+ return SimpleNamespace(returncode=0)
110
+
111
+ monkeypatch.setattr(jame_cli.subprocess, 'run', fake_run)
112
+ run_cli(monkeypatch, capsys, 'update')
113
+
114
+ assert calls[0][-2:] == ['c2e', 'jame']
115
+ assert '--pre' not in calls[0]
116
+ assert 'mapres' not in calls[0]
@@ -1,151 +0,0 @@
1
- '''Command-line entry point for JAMe.'''
2
-
3
- import argparse
4
- import json
5
- import sys
6
-
7
- from jame import Jame
8
-
9
-
10
- _COMMANDS = {
11
- 'archive',
12
- 'binary',
13
- 'binwalk',
14
- 'caesar',
15
- 'crypto',
16
- 'file-type',
17
- 'find-flag',
18
- 'forensics',
19
- 'scan',
20
- 'solve',
21
- 'stego',
22
- 'strings',
23
- }
24
-
25
-
26
- def _add_path_arguments(command: argparse.ArgumentParser) -> None:
27
- command.add_argument('path', help='Path to the file to analyze')
28
- command.add_argument('--verbose', action='store_true', help='Print structured result details')
29
-
30
-
31
- def _build_parser() -> argparse.ArgumentParser:
32
- parser = argparse.ArgumentParser(
33
- prog='jame',
34
- description='Just Another Method of Exploitation',
35
- )
36
- subparsers = parser.add_subparsers(dest='command', required=True)
37
-
38
- solve = subparsers.add_parser('solve', help='Find a flag and extract supported archives')
39
- _add_path_arguments(solve)
40
- solve.add_argument(
41
- '--format',
42
- default='flag{*}',
43
- help="Flag pattern; '*' matches any length and '*N' matches exactly N characters",
44
- )
45
- solve.add_argument('--max-depth', type=int, default=3, help='Archive extraction depth')
46
-
47
- find_flag = subparsers.add_parser('find-flag', help='Find a flag matching a pattern')
48
- _add_path_arguments(find_flag)
49
- find_flag.add_argument(
50
- '--format',
51
- default='flag{*}',
52
- help="Flag pattern; '*' matches any length and '*N' matches exactly N characters",
53
- )
54
-
55
- for name, description in (
56
- ('scan', 'Run all available analyzers'),
57
- ('crypto', 'Analyze crypto indicators'),
58
- ('binary', 'Analyze binary indicators'),
59
- ('stego', 'Check for steganography indicators'),
60
- ('forensics', 'Run forensic checks'),
61
- ('file-type', 'Detect the file type'),
62
- ('strings', 'Extract printable strings'),
63
- ('binwalk', 'Run binwalk when installed'),
64
- ):
65
- command = subparsers.add_parser(name, help=description)
66
- _add_path_arguments(command)
67
-
68
- archive = subparsers.add_parser('archive', help='Extract supported archives')
69
- _add_path_arguments(archive)
70
- archive.add_argument('--max-depth', type=int, default=3, help='Archive extraction depth')
71
-
72
- caesar = subparsers.add_parser('caesar', help='Decode Caesar-shifted text')
73
- caesar.add_argument('text', help='Text to decode')
74
- caesar.add_argument('--shift', default='brute', help="Integer shift or 'brute'")
75
- caesar.add_argument('--verbose', action='store_true', help='Print structured result details')
76
-
77
- return parser
78
-
79
-
80
- def _print_result(result: object, verbose: bool) -> None:
81
- if verbose:
82
- to_dict = getattr(result, 'to_dict', None)
83
- output = to_dict() if callable(to_dict) else result
84
- print(json.dumps(output, indent=2, default=str))
85
- elif hasattr(result, 'summary'):
86
- print(result.summary)
87
- elif result is None:
88
- print('No flag found.')
89
- elif isinstance(result, list):
90
- print('\n'.join(str(item) for item in result) if result else 'No results.')
91
- elif isinstance(result, (dict, tuple)):
92
- print(json.dumps(result, indent=2, default=str))
93
- else:
94
- print(result)
95
-
96
-
97
- def main() -> None:
98
- '''Parse command-line options and run the requested JAMe method.'''
99
- argv = sys.argv[1:]
100
- if argv and argv[0] not in _COMMANDS and argv[0] not in {'-h', '--help'}:
101
- argv.insert(0, 'solve')
102
-
103
- parser = _build_parser()
104
- args = parser.parse_args(argv)
105
- max_depth = getattr(args, 'max_depth', 3)
106
- engine = Jame(
107
- flag_patterns=[getattr(args, 'format', 'flag{*}')],
108
- max_depth=max_depth,
109
- verbose=args.verbose,
110
- )
111
-
112
- if args.command == 'solve':
113
- result = engine.autoSolve(
114
- args.path,
115
- format=args.format,
116
- maxDepth=args.max_depth,
117
- verbose=args.verbose,
118
- )
119
- elif args.command == 'find-flag':
120
- result = engine.findFlag(args.path, args.format)
121
- elif args.command == 'scan':
122
- result = engine.scanFile(args.path)
123
- elif args.command == 'crypto':
124
- result = engine.cryptoAnalyze(args.path)
125
- elif args.command == 'binary':
126
- result = engine.binaryAnalyze(args.path)
127
- elif args.command == 'stego':
128
- result = engine.stegoAnalyze(args.path)
129
- elif args.command == 'forensics':
130
- result = engine.forensicsAnalyze(args.path)
131
- elif args.command == 'archive':
132
- engine.setMaxDepth(args.max_depth)
133
- result = engine.archiveExtract(args.path)
134
- elif args.command == 'file-type':
135
- result = engine.getFileType(args.path)
136
- elif args.command == 'strings':
137
- result = engine.runStrings(args.path)
138
- elif args.command == 'binwalk':
139
- result = engine.runBinwalk(args.path)
140
- else:
141
- try:
142
- shift = 'brute' if args.shift == 'brute' else int(args.shift)
143
- except ValueError:
144
- parser.error("--shift must be an integer or 'brute'")
145
- result = engine.decodeCaesar(args.text, shift)
146
-
147
- _print_result(result, args.verbose)
148
-
149
-
150
- if __name__ == '__main__':
151
- main()
@@ -1,57 +0,0 @@
1
- '''Integration tests for the JAMe command-line interface.'''
2
-
3
- import json
4
- import sys
5
-
6
- from jame.cli.jame_cli import main
7
-
8
-
9
- def run_cli(monkeypatch, capsys, *arguments: str) -> str:
10
- monkeypatch.setattr(sys, 'argv', ['jame', *arguments])
11
- main()
12
- return capsys.readouterr().out
13
-
14
-
15
- def test_cli_uses_numbered_wildcard_for_exact_length(monkeypatch, capsys):
16
- '''Treat numbered wildcard length as exact and unnumbered stars as open-ended.'''
17
- unbounded = run_cli(
18
- monkeypatch,
19
- capsys,
20
- 'tests/files/challenge.txt',
21
- '--format',
22
- 'hcr{*****}',
23
- )
24
- assert 'hcr{abcd}' in unbounded
25
-
26
- exact = run_cli(
27
- monkeypatch,
28
- capsys,
29
- 'tests/files/challenge.txt',
30
- '--format',
31
- 'hcr{*5}',
32
- )
33
- assert 'No flag extracted.' in exact
34
- assert 'hcr{abcd}' not in exact
35
-
36
-
37
- def test_cli_verbose_prints_structured_analyzer_details(monkeypatch, capsys):
38
- '''Expose structured result details with the verbose flag.'''
39
- output = run_cli(
40
- monkeypatch,
41
- capsys,
42
- 'crypto',
43
- 'tests/files/strings.txt',
44
- '--verbose',
45
- )
46
-
47
- result = json.loads(output)
48
- assert result['file_path'] == 'tests/files/strings.txt'
49
- assert 'findings' in result
50
-
51
-
52
- def test_cli_supports_caesar_method(monkeypatch, capsys):
53
- '''Expose the Caesar decoder as a CLI command.'''
54
- output = run_cli(monkeypatch, capsys, 'caesar', 'Khoor', '--shift', '3')
55
-
56
- result = json.loads(output)
57
- assert result == {'shift': 3, 'decoded': 'Hello'}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes