lupaxa-github-token-validator 0.1.0__py3-none-any.whl
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.
- lupaxa/github_token_validator/__init__.py +7 -0
- lupaxa/github_token_validator/__main__.py +8 -0
- lupaxa/github_token_validator/classify.py +52 -0
- lupaxa/github_token_validator/cli.py +150 -0
- lupaxa/github_token_validator/client.py +131 -0
- lupaxa/github_token_validator/constants.py +37 -0
- lupaxa/github_token_validator/probes.py +478 -0
- lupaxa/github_token_validator/report.py +221 -0
- lupaxa/github_token_validator/scopes.py +98 -0
- lupaxa_github_token_validator-0.1.0.dist-info/METADATA +206 -0
- lupaxa_github_token_validator-0.1.0.dist-info/RECORD +15 -0
- lupaxa_github_token_validator-0.1.0.dist-info/WHEEL +5 -0
- lupaxa_github_token_validator-0.1.0.dist-info/entry_points.txt +3 -0
- lupaxa_github_token_validator-0.1.0.dist-info/licenses/LICENCE +21 -0
- lupaxa_github_token_validator-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Local descriptions for scopes GitHub returns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
SCOPE_ALLOWS: dict[str, str] = {
|
|
6
|
+
"repo": (
|
|
7
|
+
"Full read and write access to public and private repository code, "
|
|
8
|
+
"statuses, invitations, collaborators, deployments, and webhooks, "
|
|
9
|
+
"plus related organisation resources that this scope includes"
|
|
10
|
+
),
|
|
11
|
+
"repo:status": ("Read and write commit statuses without repository code access"),
|
|
12
|
+
"repo_deployment": (
|
|
13
|
+
"Read and write deployment statuses without repository code access"
|
|
14
|
+
),
|
|
15
|
+
"public_repo": (
|
|
16
|
+
"Read and write access limited to public repositories, "
|
|
17
|
+
"including starring public repositories"
|
|
18
|
+
),
|
|
19
|
+
"repo:invite": (
|
|
20
|
+
"Accept or decline repository collaboration invitations without code access"
|
|
21
|
+
),
|
|
22
|
+
"security_events": (
|
|
23
|
+
"Read and write code-scanning security events without repository code access"
|
|
24
|
+
),
|
|
25
|
+
"admin:repo_hook": "Read, write, ping, and delete repository hooks",
|
|
26
|
+
"write:repo_hook": "Read, write, and ping repository hooks",
|
|
27
|
+
"read:repo_hook": "Read and ping repository hooks",
|
|
28
|
+
"admin:org": (
|
|
29
|
+
"Full control of the organisation, its teams, projects, and memberships"
|
|
30
|
+
),
|
|
31
|
+
"write:org": "Read and write organisation membership and organisation projects",
|
|
32
|
+
"read:org": (
|
|
33
|
+
"Read organisation membership, organisation projects, and team membership"
|
|
34
|
+
),
|
|
35
|
+
"admin:public_key": "Full control of public keys",
|
|
36
|
+
"write:public_key": "Create, list, and view public keys",
|
|
37
|
+
"read:public_key": "List and view public keys",
|
|
38
|
+
"admin:org_hook": (
|
|
39
|
+
"Read, write, ping, and delete organisation hooks created by this token"
|
|
40
|
+
),
|
|
41
|
+
"gist": "Write gists",
|
|
42
|
+
"notifications": (
|
|
43
|
+
"Read notifications, mark threads read, watch or unwatch repositories, "
|
|
44
|
+
"and manage thread subscriptions"
|
|
45
|
+
),
|
|
46
|
+
"user": "Read and write profile info. Includes `user:email` and `user:follow`",
|
|
47
|
+
"read:user": "Read profile data",
|
|
48
|
+
"user:email": "Read email addresses",
|
|
49
|
+
"user:follow": "Follow and unfollow users",
|
|
50
|
+
"project": "Read and write user and organisation projects",
|
|
51
|
+
"read:project": "Read user and organisation projects",
|
|
52
|
+
"delete_repo": "Delete repositories the token can administer",
|
|
53
|
+
"write:packages": "Upload and publish GitHub Packages",
|
|
54
|
+
"read:packages": "Download and install GitHub Packages",
|
|
55
|
+
"delete:packages": "Delete GitHub Packages",
|
|
56
|
+
"admin:gpg_key": "Full control of GPG keys",
|
|
57
|
+
"write:gpg_key": "Create, list, and view GPG keys",
|
|
58
|
+
"read:gpg_key": "List and view GPG keys",
|
|
59
|
+
"admin:ssh_signing_key": "Full control of SSH signing keys",
|
|
60
|
+
"write:ssh_signing_key": "Create, list, and view SSH signing keys",
|
|
61
|
+
"read:ssh_signing_key": "List and view SSH signing keys",
|
|
62
|
+
"codespace": "Create and manage codespaces",
|
|
63
|
+
"workflow": "Add and update GitHub Actions workflow files",
|
|
64
|
+
"read:audit_log": "Read audit log data",
|
|
65
|
+
"offline_access": "Request an expiring access token and a refresh token",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def scope_names(header: str | None) -> tuple[str, ...]:
|
|
70
|
+
"""Split ``X-OAuth-Scopes`` in header order."""
|
|
71
|
+
if header is None:
|
|
72
|
+
return ("none",)
|
|
73
|
+
names = tuple(part.strip() for part in header.split(",") if part.strip())
|
|
74
|
+
if not names:
|
|
75
|
+
return ("none",)
|
|
76
|
+
return names
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def allows_text(scope: str) -> str:
|
|
80
|
+
"""Return the local sentence for ``scope``."""
|
|
81
|
+
if scope == "none":
|
|
82
|
+
return "GitHub sent no scopes."
|
|
83
|
+
return SCOPE_ALLOWS.get(scope, "No local description.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def accepted_permission_rows(header: str | None) -> tuple[tuple[str, str], ...]:
|
|
87
|
+
"""Split ``X-Accepted-GitHub-Permissions`` into permission and access."""
|
|
88
|
+
if header is None:
|
|
89
|
+
return (("none", "GitHub sent no accepted permissions."),)
|
|
90
|
+
rows: list[tuple[str, str]] = []
|
|
91
|
+
for part in header.replace(",", ";").split(";"):
|
|
92
|
+
name, separator, access = part.strip().partition("=")
|
|
93
|
+
if separator != "=" or name == "" or access.strip() == "":
|
|
94
|
+
continue
|
|
95
|
+
rows.append((name, access.strip()))
|
|
96
|
+
if not rows:
|
|
97
|
+
return (("none", "GitHub sent no accepted permissions."),)
|
|
98
|
+
return tuple(rows)
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lupaxa-github-token-validator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Report what a GitHub token is allowed to do.
|
|
5
|
+
Author: The Lupaxa Project
|
|
6
|
+
Maintainer: The Lupaxa Project
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/lupaxa-gh-toolbox/github-token-validator
|
|
9
|
+
Project-URL: Repository, https://github.com/lupaxa-gh-toolbox/github-token-validator
|
|
10
|
+
Project-URL: Issues, https://github.com/lupaxa-gh-toolbox/github-token-validator/issues
|
|
11
|
+
Keywords: github,permissions,token
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
27
|
+
Classifier: Topic :: Software Development
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENCE
|
|
31
|
+
Requires-Dist: colored<3.0.0,>=2.3.2
|
|
32
|
+
Requires-Dist: requests<3.0.0,>=2.32.0
|
|
33
|
+
Requires-Dist: rich<15.0.0,>=14.0.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: build<2.0.0,>=1.2.2; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy<2.0.0,>=1.17.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pre-commit<5.0.0,>=4.2.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest<9.0.0,>=8.4.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.2.0; extra == "dev"
|
|
40
|
+
Requires-Dist: ruff<1.0.0,>=0.12.0; extra == "dev"
|
|
41
|
+
Requires-Dist: twine<7.0.0,>=6.1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: types-setuptools<81.0.0,>=80.1.6; extra == "dev"
|
|
43
|
+
Provides-Extra: test
|
|
44
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
45
|
+
Requires-Dist: pytest-cov>=5.0; extra == "test"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
<p align="center">
|
|
49
|
+
<a href="https://github.com/lupaxa-gh-toolbox">
|
|
50
|
+
<img src="https://raw.githubusercontent.com/the-lupaxa-project/brand-assets/master/logos/organisations/gh-toolbox/readme-logo.png" alt="Organisation Logo" />
|
|
51
|
+
</a>
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<h1 align="center">GitHub Token Validator</h1>
|
|
55
|
+
|
|
56
|
+
**GitHub Token Validator** takes a GitHub token and reports what that token is allowed to do. It sends read-only `GET` requests. It does not list repositories, organisations, or other resources, and it never prints the token.
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
Requires Python 3.11 or newer.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install lupaxa-github-token-validator
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Two commands are installed. They do the same work.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
gtv --version
|
|
70
|
+
github-token-validator --version
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The shorter `gtv` command is used in the examples below.
|
|
74
|
+
|
|
75
|
+
## Token
|
|
76
|
+
|
|
77
|
+
`gtv` reads `GITHUB_TOKEN`. `--token` overrides that variable. A blank `--token` does not fall back to the environment. Surrounding whitespace is removed before the token is classified or sent.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export GITHUB_TOKEN="your-token"
|
|
81
|
+
gtv
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
gtv --token "$OTHER_TOKEN"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Which flags a token needs
|
|
89
|
+
|
|
90
|
+
Classic, OAuth, and user-to-server tokens return their scopes in the `X-OAuth-Scopes` header, so `--org` and `--repo` are optional.
|
|
91
|
+
|
|
92
|
+
Fine-grained, installation, and unknown tokens do not, so they need at least one of those flags before any request is sent.
|
|
93
|
+
|
|
94
|
+
A refresh token makes no request.
|
|
95
|
+
|
|
96
|
+
| Prefix | Kind | Needs a target |
|
|
97
|
+
| -------------- | ----------------------------------------- | -------------- |
|
|
98
|
+
| `ghp_` | Classic personal access token | No |
|
|
99
|
+
| `github_pat_` | Fine-grained personal access token | Yes |
|
|
100
|
+
| `gho_` | OAuth access token | No |
|
|
101
|
+
| `ghu_` | GitHub App user-to-server token | No |
|
|
102
|
+
| `ghs_` | GitHub App installation or Actions token | Yes |
|
|
103
|
+
| `ghr_` | Refresh token | No |
|
|
104
|
+
| anything else | Unknown | Yes |
|
|
105
|
+
|
|
106
|
+
Passing `--org` checks only that organisation. Passing `--repo` checks only that repository. Passing both checks both. The tool never looks up further targets.
|
|
107
|
+
|
|
108
|
+
`--org` is one name. Surrounding spaces are removed. A blank name, a space inside the name, or a `/` is rejected.
|
|
109
|
+
|
|
110
|
+
`--repo` is exactly `OWNER/NAME`: one slash, both sides non-empty, and no whitespace.
|
|
111
|
+
|
|
112
|
+
## Examples
|
|
113
|
+
|
|
114
|
+
A classic, OAuth, or user-to-server token. The scope table is the permission report. Account checks still run.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
gtv
|
|
118
|
+
gtv --timeout 30
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The same token, plus one named target. Only the catalog you name is probed.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
gtv --org lupaxa-gh-toolbox
|
|
125
|
+
gtv --repo lupaxa-gh-toolbox/github-token-validator
|
|
126
|
+
gtv --org lupaxa-gh-toolbox --repo lupaxa-gh-toolbox/github-token-validator
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A fine-grained token, an installation or Actions token, or an unknown token. At least one target is required.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gtv --repo lupaxa-gh-toolbox/github-token-validator
|
|
133
|
+
gtv --org lupaxa-gh-toolbox
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Omitting both flags exits 2 and prints a message such as `Fine-grained personal access token requires --org or --repo.` The token is not included in that message.
|
|
137
|
+
|
|
138
|
+
A refresh token (`ghr_`) prints a short summary and exits 2. It does not call the API.
|
|
139
|
+
|
|
140
|
+
## Flags
|
|
141
|
+
|
|
142
|
+
| Flag | Meaning |
|
|
143
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| `-h`, `--help` | Help, then exit 0 |
|
|
145
|
+
| `-V`, `--version` | Version, then exit 0 |
|
|
146
|
+
| `-t`, `--token` | Token. Overrides `GITHUB_TOKEN`. A blank value does not fall back to the environment |
|
|
147
|
+
| `-T`, `--timeout` | Seconds per request. Default 10. Must be an integer greater than 0 |
|
|
148
|
+
| `--org NAME` | One organisation. Required for fine-grained, installation, and unknown tokens unless `--repo` is set |
|
|
149
|
+
| `--repo OWNER/NAME` | One repository, as `OWNER/NAME`. Required for those same tokens unless `--org` is set |
|
|
150
|
+
|
|
151
|
+
## Report
|
|
152
|
+
|
|
153
|
+
A completed run prints Rich tables.
|
|
154
|
+
|
|
155
|
+
**Summary.** Token kind, login from `GET /user`, the raw scope header, and the rate-limit limit, used count, remaining count, and reset time in UTC (`YYYY-MM-DD HH:MM:SS`).
|
|
156
|
+
|
|
157
|
+
**Scopes.** Classic, OAuth, and user-to-server tokens list each `X-OAuth-Scopes` value, in header order, with a short description. An unknown scope is `No local description.`
|
|
158
|
+
|
|
159
|
+
A missing or blank header is `none`, with the text `GitHub sent no scopes.`
|
|
160
|
+
|
|
161
|
+
Fine-grained tokens do not return OAuth scopes. That row says `Fine-grained tokens do not return OAuth scopes.`
|
|
162
|
+
|
|
163
|
+
Installation and Actions tokens list `X-Accepted-GitHub-Permissions` as a permission and its access, such as `read` or `write`. A missing header is `none`, with the text `GitHub sent no accepted permissions.`
|
|
164
|
+
|
|
165
|
+
**Account permissions.** Run for every API token except installation and Actions tokens, which are not user tokens:
|
|
166
|
+
|
|
167
|
+
- Profile, Emails, Following, Organisations, Org membership roles
|
|
168
|
+
- Gists, Notifications
|
|
169
|
+
- Public keys, GPG keys, SSH signing keys
|
|
170
|
+
- Packages, Repository list, Codespaces, Installations
|
|
171
|
+
|
|
172
|
+
**Target permissions.** Printed only when `--org` or `--repo` was passed.
|
|
173
|
+
|
|
174
|
+
Organisation checks: Organisation, Members, Hooks, Audit log, Actions permissions, Packages.
|
|
175
|
+
|
|
176
|
+
Repository checks: Repository, Contents, Hooks, Workflows, Deployments, Invitations, Code scanning, Commits.
|
|
177
|
+
|
|
178
|
+
List requests use `per_page=1`. Response bodies are discarded except the login on `GET /user`, so names, paths, messages, and other resource details are not printed.
|
|
179
|
+
|
|
180
|
+
| Result | Meaning |
|
|
181
|
+
| ----------------- | -------------------------------- |
|
|
182
|
+
| `granted` | HTTP 200 or 204 |
|
|
183
|
+
| `denied` | HTTP 403 |
|
|
184
|
+
| `not applicable` | HTTP 404 |
|
|
185
|
+
| `error N` | Any other HTTP status |
|
|
186
|
+
|
|
187
|
+
If GitHub rejects the token (HTTP 401), a rate limit stops the run, or a request times out or cannot connect, the run stops and exits 1.
|
|
188
|
+
|
|
189
|
+
A rate limit is HTTP 429, or HTTP 403 when `X-RateLimit-Remaining` is `0` or `Retry-After` is set. A 403 that is only a permission denial stays a `denied` row.
|
|
190
|
+
|
|
191
|
+
On the first request, only that error is printed. On a later request, the tables gathered so far are printed, then the error.
|
|
192
|
+
|
|
193
|
+
## Exit codes
|
|
194
|
+
|
|
195
|
+
| Code | Meaning |
|
|
196
|
+
| ---- | -------------------------------------------------------------------------------------------------------------------- |
|
|
197
|
+
| 0 | The report completed, including rows that are denied, not applicable, or an HTTP error |
|
|
198
|
+
| 1 | The token was rejected, the rate limit stopped the run, or a request timed out or could not connect |
|
|
199
|
+
| 2 | Missing token, refresh token, missing target, invalid `--org`, `--repo`, or `--timeout`, or an unrecognized argument |
|
|
200
|
+
| 130 | Keyboard interrupt |
|
|
201
|
+
|
|
202
|
+
An unrecognized argument is rejected without printing the argument values.
|
|
203
|
+
|
|
204
|
+
<a href="https://github.com/the-lupaxa-project">
|
|
205
|
+
<img src="https://raw.githubusercontent.com/the-lupaxa-project/brand-assets/master/logos/components/footer-for-child-orgs.svg" alt="The Lupaxa Project Footer" width="100%" />
|
|
206
|
+
</a>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
lupaxa/github_token_validator/__init__.py,sha256=Cql0GuByJlUx76_TeqM-aw3qZTgPAxfCClCTvEa3k4I,159
|
|
2
|
+
lupaxa/github_token_validator/__main__.py,sha256=9wK_yqxiq_VaeXw0_ofhcgdkbUTzzNqkEf0Gj2KAn0w,174
|
|
3
|
+
lupaxa/github_token_validator/classify.py,sha256=mh6yHEXYTVsvUkXx2nbHSyxnVxGxb_U32ZBLgZ5QMb0,1439
|
|
4
|
+
lupaxa/github_token_validator/cli.py,sha256=NpX9kFCswoLeUybJVkGPWISY-OhO4xKNZckczrsUxqs,4856
|
|
5
|
+
lupaxa/github_token_validator/client.py,sha256=9opMMpvnzyUZucBorwxfjzoOiyJSf-2x62zdm9AC2s4,4177
|
|
6
|
+
lupaxa/github_token_validator/constants.py,sha256=K3snHneZc5cw_U3N73zy7Q-g0l8KdZISuueVvDiPIOA,974
|
|
7
|
+
lupaxa/github_token_validator/probes.py,sha256=SRv5LWEjMJkupHYF4vvTlmf6VXXTRfba1M3LdefKcyM,14333
|
|
8
|
+
lupaxa/github_token_validator/report.py,sha256=zuPYdWDcLObr20N90UgSwRKMClFmUK8taGcdfUmLYaU,6686
|
|
9
|
+
lupaxa/github_token_validator/scopes.py,sha256=cVcdNtC_Vcb1rfjpBH7jTMSGHbxbER9NGS7K5Ko1KQM,4200
|
|
10
|
+
lupaxa_github_token_validator-0.1.0.dist-info/licenses/LICENCE,sha256=RXShyrlT7e281QX54JLm3YrhOlC-TJxObadJl1gH5Es,1070
|
|
11
|
+
lupaxa_github_token_validator-0.1.0.dist-info/METADATA,sha256=rjUA3TcGXnPhRx7Kl0mB53-99O_Lc_HnlaY6XzKyGAE,9819
|
|
12
|
+
lupaxa_github_token_validator-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
lupaxa_github_token_validator-0.1.0.dist-info/entry_points.txt,sha256=Hcg0m2AUnFQRvFT-pfKlXBwHOxAQ5y4f7MillSSOMB8,127
|
|
14
|
+
lupaxa_github_token_validator-0.1.0.dist-info/top_level.txt,sha256=enJZ4xGyTomypf_yq2pEZRa_69hF0ulc9sWjyYQV9I8,7
|
|
15
|
+
lupaxa_github_token_validator-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) The Lupaxa Project
|
|
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 @@
|
|
|
1
|
+
lupaxa
|