ossuary-risk 0.1.0__tar.gz → 0.1.1__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.
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/.gitignore +3 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/PKG-INFO +10 -10
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/README.md +6 -6
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/pyproject.toml +4 -4
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/sentiment/analyzer.py +20 -0
- ossuary_risk-0.1.0/validation_results.json +0 -250
- ossuary_risk-0.1.0/validation_results_100.json +0 -2438
- ossuary_risk-0.1.0/validation_results_cleaned.json +0 -566
- ossuary_risk-0.1.0/validation_results_expanded.json +0 -634
- ossuary_risk-0.1.0/validation_results_expanded_v2.json +0 -1334
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/.env.example +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/docs/methodology.md +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/scripts/t1_comparison.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/scripts/validate.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/api/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/api/main.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/cli.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/base.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/git.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/github.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/npm.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/collectors/pypi.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/db/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/db/models.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/db/session.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/scoring/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/scoring/engine.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/scoring/factors.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/scoring/reputation.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/src/ossuary/sentiment/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/tests/__init__.py +0 -0
- {ossuary_risk-0.1.0 → ossuary_risk-0.1.1}/tests/test_scoring.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ossuary-risk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: OSS Supply Chain Risk Scoring - Where abandoned packages come to rest
|
|
5
|
-
Project-URL: Homepage, https://github.com/anicka-net/ossuary
|
|
6
|
-
Project-URL: Repository, https://github.com/anicka-net/ossuary
|
|
7
|
-
Project-URL: Documentation, https://github.com/anicka-net/ossuary/blob/main/docs/methodology.md
|
|
5
|
+
Project-URL: Homepage, https://github.com/anicka-net/ossuary-risk
|
|
6
|
+
Project-URL: Repository, https://github.com/anicka-net/ossuary-risk
|
|
7
|
+
Project-URL: Documentation, https://github.com/anicka-net/ossuary-risk/blob/main/docs/methodology.md
|
|
8
8
|
Author: Anicka
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Keywords: oss,risk,scoring,security,supply-chain
|
|
@@ -165,8 +165,8 @@ Response:
|
|
|
165
165
|
|
|
166
166
|
```bash
|
|
167
167
|
# Clone
|
|
168
|
-
git clone https://github.com/anicka/ossuary.git
|
|
169
|
-
cd ossuary
|
|
168
|
+
git clone https://github.com/anicka-net/ossuary-risk.git
|
|
169
|
+
cd ossuary-risk
|
|
170
170
|
|
|
171
171
|
# Install with dev dependencies
|
|
172
172
|
pip install -e ".[dev]"
|
|
@@ -221,12 +221,12 @@ REPOS_PATH=./repos
|
|
|
221
221
|
|
|
222
222
|
## Validation
|
|
223
223
|
|
|
224
|
-
Validated on
|
|
224
|
+
Validated on 92 packages (20 incidents + 72 controls):
|
|
225
225
|
|
|
226
|
-
- **Accuracy**:
|
|
227
|
-
- **Precision**:
|
|
226
|
+
- **Accuracy**: 92.4%
|
|
227
|
+
- **Precision**: 100.0%
|
|
228
228
|
- **Recall**: 65.0%
|
|
229
|
-
- **F1 Score**: 0.
|
|
229
|
+
- **F1 Score**: 0.79
|
|
230
230
|
|
|
231
231
|
T-1 analysis confirms **100% predictive detection** of governance-detectable incidents before they occurred.
|
|
232
232
|
|
|
@@ -125,8 +125,8 @@ Response:
|
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
127
|
# Clone
|
|
128
|
-
git clone https://github.com/anicka/ossuary.git
|
|
129
|
-
cd ossuary
|
|
128
|
+
git clone https://github.com/anicka-net/ossuary-risk.git
|
|
129
|
+
cd ossuary-risk
|
|
130
130
|
|
|
131
131
|
# Install with dev dependencies
|
|
132
132
|
pip install -e ".[dev]"
|
|
@@ -181,12 +181,12 @@ REPOS_PATH=./repos
|
|
|
181
181
|
|
|
182
182
|
## Validation
|
|
183
183
|
|
|
184
|
-
Validated on
|
|
184
|
+
Validated on 92 packages (20 incidents + 72 controls):
|
|
185
185
|
|
|
186
|
-
- **Accuracy**:
|
|
187
|
-
- **Precision**:
|
|
186
|
+
- **Accuracy**: 92.4%
|
|
187
|
+
- **Precision**: 100.0%
|
|
188
188
|
- **Recall**: 65.0%
|
|
189
|
-
- **F1 Score**: 0.
|
|
189
|
+
- **F1 Score**: 0.79
|
|
190
190
|
|
|
191
191
|
T-1 analysis confirms **100% predictive detection** of governance-detectable incidents before they occurred.
|
|
192
192
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ossuary-risk"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "OSS Supply Chain Risk Scoring - Where abandoned packages come to rest"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -53,9 +53,9 @@ dev = [
|
|
|
53
53
|
ossuary = "ossuary.cli:app"
|
|
54
54
|
|
|
55
55
|
[project.urls]
|
|
56
|
-
Homepage = "https://github.com/anicka-net/ossuary"
|
|
57
|
-
Repository = "https://github.com/anicka-net/ossuary"
|
|
58
|
-
Documentation = "https://github.com/anicka-net/ossuary/blob/main/docs/methodology.md"
|
|
56
|
+
Homepage = "https://github.com/anicka-net/ossuary-risk"
|
|
57
|
+
Repository = "https://github.com/anicka-net/ossuary-risk"
|
|
58
|
+
Documentation = "https://github.com/anicka-net/ossuary-risk/blob/main/docs/methodology.md"
|
|
59
59
|
|
|
60
60
|
[tool.hatch.build.targets.wheel]
|
|
61
61
|
packages = ["src/ossuary"]
|
|
@@ -12,12 +12,29 @@ logger = logging.getLogger(__name__)
|
|
|
12
12
|
|
|
13
13
|
# Keywords indicating maintainer frustration/burnout
|
|
14
14
|
# These should be specific enough to avoid false positives on normal development discussions
|
|
15
|
+
#
|
|
16
|
+
# VALIDATION NOTE (Feb 2026):
|
|
17
|
+
# Tested against Marak Squires' actual Nov 2020 rant before colors.js/faker.js sabotage:
|
|
18
|
+
# "No More Free Works from Marak – Pay Me or Fork It. With all due respect,
|
|
19
|
+
# I am no longer going to support the Fortune 500 (and other smaller companies)
|
|
20
|
+
# with my free work."
|
|
21
|
+
#
|
|
22
|
+
# Key finding: VADER scored this as +0.676 (positive!) due to words like "support"
|
|
23
|
+
# and "opportunity". Keyword matching is essential - VADER alone would miss this.
|
|
24
|
+
#
|
|
25
|
+
# Added keywords based on this analysis:
|
|
26
|
+
# - "free work" / "my free work" - exact phrase from Marak's rant
|
|
27
|
+
# - "no longer support" - resignation signal
|
|
28
|
+
# - "stop supporting" - variation
|
|
29
|
+
#
|
|
15
30
|
FRUSTRATION_KEYWORDS = [
|
|
16
31
|
# Direct economic frustration (high signal)
|
|
17
32
|
"not getting paid",
|
|
18
33
|
"unpaid work",
|
|
19
34
|
"free labor",
|
|
20
35
|
"work for free",
|
|
36
|
+
"free work", # Added: exact phrase from Marak's 2020 rant
|
|
37
|
+
"my free work", # Added: more specific variant
|
|
21
38
|
"donating my time",
|
|
22
39
|
"corporate exploitation",
|
|
23
40
|
"open source exploitation",
|
|
@@ -28,6 +45,9 @@ FRUSTRATION_KEYWORDS = [
|
|
|
28
45
|
"stepping down",
|
|
29
46
|
"giving up on this",
|
|
30
47
|
"abandoning this project",
|
|
48
|
+
"no longer support", # Added: from Marak's "no longer going to support"
|
|
49
|
+
"stop supporting", # Added: variation
|
|
50
|
+
"stopping support", # Added: verb form variation
|
|
31
51
|
# Economic frustration (moderate signal)
|
|
32
52
|
"fortune 500",
|
|
33
53
|
"pay developers",
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"timestamp": "2026-02-01T17:38:28.683199",
|
|
3
|
-
"total": 9,
|
|
4
|
-
"accuracy": 0.8888888888888888,
|
|
5
|
-
"precision": 1.0,
|
|
6
|
-
"recall": 0.6666666666666666,
|
|
7
|
-
"f1_score": 0.8,
|
|
8
|
-
"confusion_matrix": {
|
|
9
|
-
"TP": 2,
|
|
10
|
-
"TN": 6,
|
|
11
|
-
"FP": 0,
|
|
12
|
-
"FN": 1
|
|
13
|
-
},
|
|
14
|
-
"by_attack_type": {
|
|
15
|
-
"governance_failure": {
|
|
16
|
-
"total": 1,
|
|
17
|
-
"correct": 1
|
|
18
|
-
},
|
|
19
|
-
"maintainer_sabotage": {
|
|
20
|
-
"total": 1,
|
|
21
|
-
"correct": 1
|
|
22
|
-
},
|
|
23
|
-
"account_compromise": {
|
|
24
|
-
"total": 1,
|
|
25
|
-
"correct": 0
|
|
26
|
-
},
|
|
27
|
-
"control": {
|
|
28
|
-
"total": 6,
|
|
29
|
-
"correct": 6
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"results": [
|
|
33
|
-
{
|
|
34
|
-
"case": {
|
|
35
|
-
"name": "event-stream",
|
|
36
|
-
"ecosystem": "npm",
|
|
37
|
-
"expected_outcome": "incident",
|
|
38
|
-
"attack_type": "governance_failure",
|
|
39
|
-
"incident_date": "2018-09-16",
|
|
40
|
-
"cutoff_date": "2018-09-01",
|
|
41
|
-
"notes": "Abandoned package, malicious maintainer gained access via social engineering",
|
|
42
|
-
"repo_url": null
|
|
43
|
-
},
|
|
44
|
-
"score": 80,
|
|
45
|
-
"risk_level": "CRITICAL",
|
|
46
|
-
"predicted_outcome": "risky",
|
|
47
|
-
"correct": true,
|
|
48
|
-
"classification": "TP",
|
|
49
|
-
"maintainer": "rolias",
|
|
50
|
-
"reputation_score": 15,
|
|
51
|
-
"reputation_tier": "UNKNOWN",
|
|
52
|
-
"concentration": 75.0,
|
|
53
|
-
"commits_last_year": 4,
|
|
54
|
-
"protective_factors_total": 0,
|
|
55
|
-
"error": null
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"case": {
|
|
59
|
-
"name": "colors",
|
|
60
|
-
"ecosystem": "npm",
|
|
61
|
-
"expected_outcome": "incident",
|
|
62
|
-
"attack_type": "maintainer_sabotage",
|
|
63
|
-
"incident_date": "2022-01-08",
|
|
64
|
-
"cutoff_date": "2022-01-01",
|
|
65
|
-
"notes": "Marak intentionally sabotaged as protest against Fortune 500 companies",
|
|
66
|
-
"repo_url": null
|
|
67
|
-
},
|
|
68
|
-
"score": 100,
|
|
69
|
-
"risk_level": "CRITICAL",
|
|
70
|
-
"predicted_outcome": "risky",
|
|
71
|
-
"correct": true,
|
|
72
|
-
"classification": "TP",
|
|
73
|
-
"maintainer": "Marak",
|
|
74
|
-
"reputation_score": 15,
|
|
75
|
-
"reputation_tier": "UNKNOWN",
|
|
76
|
-
"concentration": 100,
|
|
77
|
-
"commits_last_year": 0,
|
|
78
|
-
"protective_factors_total": -5,
|
|
79
|
-
"error": null
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"case": {
|
|
83
|
-
"name": "ua-parser-js",
|
|
84
|
-
"ecosystem": "npm",
|
|
85
|
-
"expected_outcome": "incident",
|
|
86
|
-
"attack_type": "account_compromise",
|
|
87
|
-
"incident_date": "2021-10-22",
|
|
88
|
-
"cutoff_date": "2021-10-01",
|
|
89
|
-
"notes": "Maintainer account compromised via email hijacking. Active project - governance metrics won't catch this.",
|
|
90
|
-
"repo_url": null
|
|
91
|
-
},
|
|
92
|
-
"score": 25,
|
|
93
|
-
"risk_level": "LOW",
|
|
94
|
-
"predicted_outcome": "safe",
|
|
95
|
-
"correct": false,
|
|
96
|
-
"classification": "FN",
|
|
97
|
-
"maintainer": "faisalman",
|
|
98
|
-
"reputation_score": 15,
|
|
99
|
-
"reputation_tier": "UNKNOWN",
|
|
100
|
-
"concentration": 80.26315789473685,
|
|
101
|
-
"commits_last_year": 152,
|
|
102
|
-
"protective_factors_total": -25,
|
|
103
|
-
"error": null
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
"case": {
|
|
107
|
-
"name": "chalk",
|
|
108
|
-
"ecosystem": "npm",
|
|
109
|
-
"expected_outcome": "safe",
|
|
110
|
-
"attack_type": null,
|
|
111
|
-
"incident_date": null,
|
|
112
|
-
"cutoff_date": null,
|
|
113
|
-
"notes": "High concentration but Sindre Sorhus has massive reputation and sponsors",
|
|
114
|
-
"repo_url": null
|
|
115
|
-
},
|
|
116
|
-
"score": 0,
|
|
117
|
-
"risk_level": "VERY_LOW",
|
|
118
|
-
"predicted_outcome": "safe",
|
|
119
|
-
"correct": true,
|
|
120
|
-
"classification": "TN",
|
|
121
|
-
"maintainer": "sindresorhus",
|
|
122
|
-
"reputation_score": 90,
|
|
123
|
-
"reputation_tier": "TIER_1",
|
|
124
|
-
"concentration": 66.66666666666666,
|
|
125
|
-
"commits_last_year": 6,
|
|
126
|
-
"protective_factors_total": -60,
|
|
127
|
-
"error": null
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"case": {
|
|
131
|
-
"name": "lodash",
|
|
132
|
-
"ecosystem": "npm",
|
|
133
|
-
"expected_outcome": "safe",
|
|
134
|
-
"attack_type": null,
|
|
135
|
-
"incident_date": null,
|
|
136
|
-
"cutoff_date": null,
|
|
137
|
-
"notes": "Very popular, historically high concentration but visible and maintained",
|
|
138
|
-
"repo_url": null
|
|
139
|
-
},
|
|
140
|
-
"score": 0,
|
|
141
|
-
"risk_level": "VERY_LOW",
|
|
142
|
-
"predicted_outcome": "safe",
|
|
143
|
-
"correct": true,
|
|
144
|
-
"classification": "TN",
|
|
145
|
-
"maintainer": "jdalton",
|
|
146
|
-
"reputation_score": 45,
|
|
147
|
-
"reputation_tier": "TIER_2",
|
|
148
|
-
"concentration": 42.857142857142854,
|
|
149
|
-
"commits_last_year": 35,
|
|
150
|
-
"protective_factors_total": -45,
|
|
151
|
-
"error": null
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
"case": {
|
|
155
|
-
"name": "express",
|
|
156
|
-
"ecosystem": "npm",
|
|
157
|
-
"expected_outcome": "safe",
|
|
158
|
-
"attack_type": null,
|
|
159
|
-
"incident_date": null,
|
|
160
|
-
"cutoff_date": null,
|
|
161
|
-
"notes": "OpenJS Foundation, multiple maintainers, very active",
|
|
162
|
-
"repo_url": null
|
|
163
|
-
},
|
|
164
|
-
"score": 0,
|
|
165
|
-
"risk_level": "VERY_LOW",
|
|
166
|
-
"predicted_outcome": "safe",
|
|
167
|
-
"correct": true,
|
|
168
|
-
"classification": "TN",
|
|
169
|
-
"maintainer": "dependabot[bot]",
|
|
170
|
-
"reputation_score": 30,
|
|
171
|
-
"reputation_tier": "TIER_2",
|
|
172
|
-
"concentration": 24.444444444444443,
|
|
173
|
-
"commits_last_year": 135,
|
|
174
|
-
"protective_factors_total": -65,
|
|
175
|
-
"error": null
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
"case": {
|
|
179
|
-
"name": "requests",
|
|
180
|
-
"ecosystem": "pypi",
|
|
181
|
-
"expected_outcome": "safe",
|
|
182
|
-
"attack_type": null,
|
|
183
|
-
"incident_date": null,
|
|
184
|
-
"cutoff_date": null,
|
|
185
|
-
"notes": "Transitioned from Kenneth Reitz to PSF, successful community handoff",
|
|
186
|
-
"repo_url": null
|
|
187
|
-
},
|
|
188
|
-
"score": 0,
|
|
189
|
-
"risk_level": "VERY_LOW",
|
|
190
|
-
"predicted_outcome": "safe",
|
|
191
|
-
"correct": true,
|
|
192
|
-
"classification": "TN",
|
|
193
|
-
"maintainer": "kennethreitz",
|
|
194
|
-
"reputation_score": 30,
|
|
195
|
-
"reputation_tier": "TIER_2",
|
|
196
|
-
"concentration": 36.36363636363637,
|
|
197
|
-
"commits_last_year": 66,
|
|
198
|
-
"protective_factors_total": -70,
|
|
199
|
-
"error": null
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
"case": {
|
|
203
|
-
"name": "urllib3",
|
|
204
|
-
"ecosystem": "pypi",
|
|
205
|
-
"expected_outcome": "safe",
|
|
206
|
-
"attack_type": null,
|
|
207
|
-
"incident_date": null,
|
|
208
|
-
"cutoff_date": null,
|
|
209
|
-
"notes": "Org-owned, multiple maintainers, good governance",
|
|
210
|
-
"repo_url": null
|
|
211
|
-
},
|
|
212
|
-
"score": 0,
|
|
213
|
-
"risk_level": "VERY_LOW",
|
|
214
|
-
"predicted_outcome": "safe",
|
|
215
|
-
"correct": true,
|
|
216
|
-
"classification": "TN",
|
|
217
|
-
"maintainer": "shazow",
|
|
218
|
-
"reputation_score": 30,
|
|
219
|
-
"reputation_tier": "TIER_2",
|
|
220
|
-
"concentration": 36.36363636363637,
|
|
221
|
-
"commits_last_year": 110,
|
|
222
|
-
"protective_factors_total": -80,
|
|
223
|
-
"error": null
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"case": {
|
|
227
|
-
"name": "django",
|
|
228
|
-
"ecosystem": "pypi",
|
|
229
|
-
"expected_outcome": "safe",
|
|
230
|
-
"attack_type": null,
|
|
231
|
-
"incident_date": null,
|
|
232
|
-
"cutoff_date": null,
|
|
233
|
-
"notes": "Django Software Foundation, many contributors, professional governance",
|
|
234
|
-
"repo_url": null
|
|
235
|
-
},
|
|
236
|
-
"score": 0,
|
|
237
|
-
"risk_level": "VERY_LOW",
|
|
238
|
-
"predicted_outcome": "safe",
|
|
239
|
-
"correct": true,
|
|
240
|
-
"classification": "TN",
|
|
241
|
-
"maintainer": "nessita",
|
|
242
|
-
"reputation_score": 30,
|
|
243
|
-
"reputation_tier": "TIER_2",
|
|
244
|
-
"concentration": 16.666666666666664,
|
|
245
|
-
"commits_last_year": 1602,
|
|
246
|
-
"protective_factors_total": -45,
|
|
247
|
-
"error": null
|
|
248
|
-
}
|
|
249
|
-
]
|
|
250
|
-
}
|