secretshield 0.4.1__tar.gz → 0.4.2__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.
- {secretshield-0.4.1/secretshield.egg-info → secretshield-0.4.2}/PKG-INFO +8 -2
- {secretshield-0.4.1 → secretshield-0.4.2}/README.md +7 -1
- {secretshield-0.4.1 → secretshield-0.4.2}/pyproject.toml +3 -2
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/__init__.py +1 -1
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/cli.py +19 -0
- {secretshield-0.4.1 → secretshield-0.4.2/secretshield.egg-info}/PKG-INFO +8 -2
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield.egg-info/SOURCES.txt +1 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield.egg-info/entry_points.txt +1 -0
- secretshield-0.4.2/tests/test_ss_alias_and_creator.py +147 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/LICENSE +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/autofix/__init__.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/autofix/env.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/autofix/fixer.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/autofix/gitignore.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/autofix/python.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/baseline.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/config.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/detector.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/git/__init__.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/git/hooks.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/github/__init__.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/github/actions.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/guardian.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/notifications.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/patterns.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/project_config.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield/redactor.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield.egg-info/dependency_links.txt +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield.egg-info/requires.txt +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/secretshield.egg-info/top_level.txt +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/setup.cfg +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_autofix.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_baseline.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_detector.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_git_hooks.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_github_action.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_logging.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_project_config.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_redactor.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_scan.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_stdout.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_v030_cli_integration.py +0 -0
- {secretshield-0.4.1 → secretshield-0.4.2}/tests/test_v040_cli_integration.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secretshield
|
|
3
|
-
Version: 0.4.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Stop secrets before they leak -- runtime redaction, multi-language scanning, auto-fix, Git hooks, and CI, all in one CLI.
|
|
5
5
|
Author: Samarth Chugh (Sam3360)
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/Sam3360/secretshield
|
|
@@ -106,6 +106,10 @@ Set a whole project up in one step (config file + Git hook + CI):
|
|
|
106
106
|
secretshield init
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
> Typing `secretshield` a lot? Every command also works with the short
|
|
110
|
+
> alias `ss` — `ss scan .`, `ss init`, `ss --fix`, all identical to
|
|
111
|
+
> their `secretshield` equivalents.
|
|
112
|
+
|
|
109
113
|
## What SecretShield does
|
|
110
114
|
|
|
111
115
|
SecretShield isn't just one trick — it's five layers that cover the
|
|
@@ -179,6 +183,8 @@ redact("aws_key=AKIAABCDEFGHIJKLMNOP")
|
|
|
179
183
|
|
|
180
184
|
## CLI reference
|
|
181
185
|
|
|
186
|
+
*(Every command below also works via the shorter `ss` alias — `ss scan .` is identical to `secretshield scan .`.)*
|
|
187
|
+
|
|
182
188
|
### `secretshield init` — set a project up in one step
|
|
183
189
|
|
|
184
190
|
```bash
|
|
@@ -80,6 +80,10 @@ Set a whole project up in one step (config file + Git hook + CI):
|
|
|
80
80
|
secretshield init
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
> Typing `secretshield` a lot? Every command also works with the short
|
|
84
|
+
> alias `ss` — `ss scan .`, `ss init`, `ss --fix`, all identical to
|
|
85
|
+
> their `secretshield` equivalents.
|
|
86
|
+
|
|
83
87
|
## What SecretShield does
|
|
84
88
|
|
|
85
89
|
SecretShield isn't just one trick — it's five layers that cover the
|
|
@@ -153,6 +157,8 @@ redact("aws_key=AKIAABCDEFGHIJKLMNOP")
|
|
|
153
157
|
|
|
154
158
|
## CLI reference
|
|
155
159
|
|
|
160
|
+
*(Every command below also works via the shorter `ss` alias — `ss scan .` is identical to `secretshield scan .`.)*
|
|
161
|
+
|
|
156
162
|
### `secretshield init` — set a project up in one step
|
|
157
163
|
|
|
158
164
|
```bash
|
|
@@ -324,4 +330,4 @@ If you find this project useful, consider [supporting its development through Gi
|
|
|
324
330
|
|
|
325
331
|
## License
|
|
326
332
|
|
|
327
|
-
MIT — see [LICENSE](LICENSE).
|
|
333
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "secretshield"
|
|
7
|
-
version = "0.4.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.4.2"
|
|
8
|
+
description = "Stop secrets before they leak -- runtime redaction, multi-language scanning, auto-fix, Git hooks, and CI, all in one CLI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -39,6 +39,7 @@ Issues = "https://github.com/Sam3360/secretshield/issues"
|
|
|
39
39
|
|
|
40
40
|
[project.scripts]
|
|
41
41
|
secretshield = "secretshield.cli:main"
|
|
42
|
+
ss = "secretshield.cli:main"
|
|
42
43
|
|
|
43
44
|
[tool.setuptools.packages.find]
|
|
44
45
|
include = ["secretshield*"]
|
|
@@ -726,6 +726,22 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
726
726
|
# --- argparse wiring -----------------------------------------------------
|
|
727
727
|
|
|
728
728
|
|
|
729
|
+
CREATOR_MESSAGE = "SecretShield\nCreated by Samarth Chugh (Sam3360)\ngithub.com/Sam3360"
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
class _CreatorAction(argparse.Action):
|
|
733
|
+
"""Prints creator info and exits -- mirrors argparse's built-in
|
|
734
|
+
`action="version"` behavior so `--creator` works the same way
|
|
735
|
+
`--version` does, with no subcommand required."""
|
|
736
|
+
|
|
737
|
+
def __init__(self, option_strings, dest, nargs=0, **kwargs):
|
|
738
|
+
super().__init__(option_strings, dest, nargs=nargs, **kwargs)
|
|
739
|
+
|
|
740
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
741
|
+
print(CREATOR_MESSAGE)
|
|
742
|
+
parser.exit()
|
|
743
|
+
|
|
744
|
+
|
|
729
745
|
def build_parser() -> argparse.ArgumentParser:
|
|
730
746
|
parser = argparse.ArgumentParser(
|
|
731
747
|
prog="secretshield",
|
|
@@ -737,6 +753,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
737
753
|
parser.add_argument(
|
|
738
754
|
"--version", action="version", version=f"secretshield {__version__}"
|
|
739
755
|
)
|
|
756
|
+
parser.add_argument(
|
|
757
|
+
"--creator", action=_CreatorAction, help="Show who made this and exit."
|
|
758
|
+
)
|
|
740
759
|
|
|
741
760
|
subparsers = parser.add_subparsers(dest="command")
|
|
742
761
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secretshield
|
|
3
|
-
Version: 0.4.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Stop secrets before they leak -- runtime redaction, multi-language scanning, auto-fix, Git hooks, and CI, all in one CLI.
|
|
5
5
|
Author: Samarth Chugh (Sam3360)
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/Sam3360/secretshield
|
|
@@ -106,6 +106,10 @@ Set a whole project up in one step (config file + Git hook + CI):
|
|
|
106
106
|
secretshield init
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
> Typing `secretshield` a lot? Every command also works with the short
|
|
110
|
+
> alias `ss` — `ss scan .`, `ss init`, `ss --fix`, all identical to
|
|
111
|
+
> their `secretshield` equivalents.
|
|
112
|
+
|
|
109
113
|
## What SecretShield does
|
|
110
114
|
|
|
111
115
|
SecretShield isn't just one trick — it's five layers that cover the
|
|
@@ -179,6 +183,8 @@ redact("aws_key=AKIAABCDEFGHIJKLMNOP")
|
|
|
179
183
|
|
|
180
184
|
## CLI reference
|
|
181
185
|
|
|
186
|
+
*(Every command below also works via the shorter `ss` alias — `ss scan .` is identical to `secretshield scan .`.)*
|
|
187
|
+
|
|
182
188
|
### `secretshield init` — set a project up in one step
|
|
183
189
|
|
|
184
190
|
```bash
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for v0.4.2: the `ss` short CLI alias and the `--creator` easter
|
|
3
|
+
egg. Both `ss` and `secretshield` are registered as separate console
|
|
4
|
+
scripts pointing to the exact same `secretshield.cli:main` function, so
|
|
5
|
+
most equivalence is tested by exercising `main()` directly; a couple of
|
|
6
|
+
tests also verify the actual installed executables behave identically.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import shutil
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from secretshield.cli import CREATOR_MESSAGE, build_parser, main
|
|
18
|
+
|
|
19
|
+
FAKE_SECRET = "ssAliasFakeSecretForTests123456789"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# --- --creator, via main() (fast, no subprocess) -----------------------------
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_creator_flag_exits_zero():
|
|
26
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
27
|
+
main(["--creator"])
|
|
28
|
+
assert exc_info.value.code in (0, None)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_creator_flag_prints_expected_content(capsys):
|
|
32
|
+
with pytest.raises(SystemExit):
|
|
33
|
+
main(["--creator"])
|
|
34
|
+
out = capsys.readouterr().out
|
|
35
|
+
assert "Samarth Chugh (Sam3360)" in out
|
|
36
|
+
assert "github.com/Sam3360" in out
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_creator_message_constant_matches_requirements():
|
|
40
|
+
assert "Samarth Chugh (Sam3360)" in CREATOR_MESSAGE
|
|
41
|
+
assert "github.com/Sam3360" in CREATOR_MESSAGE
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_creator_flag_is_not_overly_verbose():
|
|
45
|
+
# Polished, not a wall of text.
|
|
46
|
+
assert CREATOR_MESSAGE.count("\n") <= 3
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_creator_works_regardless_of_subcommand_position():
|
|
50
|
+
# --creator, like --version, should short-circuit before any
|
|
51
|
+
# subcommand dispatch is required.
|
|
52
|
+
parser = build_parser()
|
|
53
|
+
with pytest.raises(SystemExit):
|
|
54
|
+
parser.parse_args(["--creator"])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- console scripts: both entry points exist and are wired identically ----
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_pyproject_declares_both_console_scripts():
|
|
61
|
+
try:
|
|
62
|
+
import tomllib # Python 3.11+
|
|
63
|
+
except ModuleNotFoundError:
|
|
64
|
+
import tomli as tomllib # Python 3.10 fallback
|
|
65
|
+
|
|
66
|
+
with open("pyproject.toml", "rb") as f:
|
|
67
|
+
data = tomllib.load(f)
|
|
68
|
+
scripts = data["project"]["scripts"]
|
|
69
|
+
assert scripts.get("secretshield") == "secretshield.cli:main"
|
|
70
|
+
assert scripts.get("ss") == "secretshield.cli:main"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _find_executable(name: str) -> str | None:
|
|
74
|
+
return shutil.which(name)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.skipif(
|
|
78
|
+
_find_executable("ss") is None or _find_executable("secretshield") is None,
|
|
79
|
+
reason="ss/secretshield not installed as real executables in this environment",
|
|
80
|
+
)
|
|
81
|
+
def test_installed_ss_and_secretshield_creator_output_match():
|
|
82
|
+
result_full = subprocess.run(
|
|
83
|
+
["secretshield", "--creator"], capture_output=True, text=True
|
|
84
|
+
)
|
|
85
|
+
result_short = subprocess.run(["ss", "--creator"], capture_output=True, text=True)
|
|
86
|
+
assert result_full.returncode == 0
|
|
87
|
+
assert result_short.returncode == 0
|
|
88
|
+
assert result_full.stdout == result_short.stdout
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.skipif(
|
|
92
|
+
_find_executable("ss") is None or _find_executable("secretshield") is None,
|
|
93
|
+
reason="ss/secretshield not installed as real executables in this environment",
|
|
94
|
+
)
|
|
95
|
+
def test_installed_ss_and_secretshield_version_match():
|
|
96
|
+
result_full = subprocess.run(
|
|
97
|
+
["secretshield", "--version"], capture_output=True, text=True
|
|
98
|
+
)
|
|
99
|
+
result_short = subprocess.run(["ss", "--version"], capture_output=True, text=True)
|
|
100
|
+
assert result_full.stdout == result_short.stdout
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.skipif(
|
|
104
|
+
_find_executable("ss") is None or _find_executable("secretshield") is None,
|
|
105
|
+
reason="ss/secretshield not installed as real executables in this environment",
|
|
106
|
+
)
|
|
107
|
+
def test_installed_ss_and_secretshield_help_match():
|
|
108
|
+
result_full = subprocess.run(
|
|
109
|
+
["secretshield", "--help"], capture_output=True, text=True
|
|
110
|
+
)
|
|
111
|
+
result_short = subprocess.run(["ss", "--help"], capture_output=True, text=True)
|
|
112
|
+
assert result_full.stdout == result_short.stdout
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.skipif(
|
|
116
|
+
_find_executable("ss") is None or _find_executable("secretshield") is None,
|
|
117
|
+
reason="ss/secretshield not installed as real executables in this environment",
|
|
118
|
+
)
|
|
119
|
+
def test_installed_ss_scan_behaves_like_secretshield_scan(tmp_path):
|
|
120
|
+
(tmp_path / "app.py").write_text(f'API_KEY = "{FAKE_SECRET}"\n')
|
|
121
|
+
|
|
122
|
+
result_full = subprocess.run(
|
|
123
|
+
["secretshield", "scan", str(tmp_path)], capture_output=True, text=True
|
|
124
|
+
)
|
|
125
|
+
result_short = subprocess.run(
|
|
126
|
+
["ss", "scan", str(tmp_path)], capture_output=True, text=True
|
|
127
|
+
)
|
|
128
|
+
assert result_full.returncode == result_short.returncode == 1
|
|
129
|
+
assert result_full.stdout == result_short.stdout
|
|
130
|
+
assert FAKE_SECRET not in result_full.stdout
|
|
131
|
+
assert FAKE_SECRET not in result_short.stdout
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# --- existing commands unaffected -------------------------------------------
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_existing_scan_command_still_works_after_creator_addition(tmp_path):
|
|
138
|
+
app = tmp_path / "app.py"
|
|
139
|
+
app.write_text(f'API_KEY = "{FAKE_SECRET}"\n')
|
|
140
|
+
exit_code = main(["scan", str(app)])
|
|
141
|
+
assert exit_code == 1
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_version_flag_still_works():
|
|
145
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
146
|
+
main(["--version"])
|
|
147
|
+
assert exc_info.value.code in (0, None)
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|