rithik 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.
- rithik-0.1.1/.gitattributes +4 -0
- rithik-0.1.1/.github/workflows/ci.yml +68 -0
- rithik-0.1.1/.github/workflows/publish.yml +80 -0
- rithik-0.1.1/.gitignore +10 -0
- rithik-0.1.1/LICENSE +21 -0
- rithik-0.1.1/PKG-INFO +253 -0
- rithik-0.1.1/README.md +233 -0
- rithik-0.1.1/docs/ARCHITECTURE.md +63 -0
- rithik-0.1.1/docs/EVAL.md +43 -0
- rithik-0.1.1/docs/PROJECT_STATE.md +57 -0
- rithik-0.1.1/docs/RELEASING.md +96 -0
- rithik-0.1.1/docs/ROADMAP.md +12 -0
- rithik-0.1.1/js/bin/rithik.js +5 -0
- rithik-0.1.1/js/lib/card.js +160 -0
- rithik-0.1.1/js/lib/cli.js +605 -0
- rithik-0.1.1/js/lib/pyjson.js +147 -0
- rithik-0.1.1/js/lib/pytext.js +170 -0
- rithik-0.1.1/js/lib/scam/engine.js +199 -0
- rithik-0.1.1/js/lib/scam/index.js +8 -0
- rithik-0.1.1/js/lib/scam/model.js +56 -0
- rithik-0.1.1/js/lib/scam/pycompat.js +564 -0
- rithik-0.1.1/js/lib/scam/rules.js +1005 -0
- rithik-0.1.1/js/lib/scam/urls.js +475 -0
- rithik-0.1.1/js/lib/scam/weights.js +111 -0
- rithik-0.1.1/js/lib/stdin.js +210 -0
- rithik-0.1.1/js/lib/term.js +75 -0
- rithik-0.1.1/js/lib/version.js +7 -0
- rithik-0.1.1/js/test/cli.parity.test.js +498 -0
- rithik-0.1.1/js/test/golden/cli.json +114 -0
- rithik-0.1.1/js/test/golden/scam.json +4639 -0
- rithik-0.1.1/js/test/scam.parity.test.js +171 -0
- rithik-0.1.1/package.json +39 -0
- rithik-0.1.1/pyproject.toml +50 -0
- rithik-0.1.1/scripts/eval.py +99 -0
- rithik-0.1.1/scripts/export_golden.py +189 -0
- rithik-0.1.1/src/rithik/__init__.py +3 -0
- rithik-0.1.1/src/rithik/__main__.py +6 -0
- rithik-0.1.1/src/rithik/_term.py +106 -0
- rithik-0.1.1/src/rithik/card.py +154 -0
- rithik-0.1.1/src/rithik/cli.py +297 -0
- rithik-0.1.1/src/rithik/scam/__init__.py +9 -0
- rithik-0.1.1/src/rithik/scam/engine.py +151 -0
- rithik-0.1.1/src/rithik/scam/model.py +45 -0
- rithik-0.1.1/src/rithik/scam/rules.py +1018 -0
- rithik-0.1.1/src/rithik/scam/urls.py +352 -0
- rithik-0.1.1/src/rithik/scam/weights.py +106 -0
- rithik-0.1.1/tests/data/README.md +5 -0
- rithik-0.1.1/tests/data/dev.jsonl +80 -0
- rithik-0.1.1/tests/data/test.jsonl +80 -0
- rithik-0.1.1/tests/test_cli.py +421 -0
- rithik-0.1.1/uv.lock +229 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: test (${{ matrix.os }}, Python ${{ matrix.python }})
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
# One failing cell should not hide whether the others pass.
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
os: [ubuntu-latest, windows-latest]
|
|
19
|
+
# The oldest supported Python and the newest one.
|
|
20
|
+
python: ["3.9", "3.13"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
|
|
24
|
+
# setup-uv stopped publishing floating major tags at v8, so this has to name a full
|
|
25
|
+
# release.
|
|
26
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
27
|
+
|
|
28
|
+
- name: Test
|
|
29
|
+
run: uv run --python ${{ matrix.python }} pytest -q
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
|
|
33
|
+
run: |
|
|
34
|
+
uv run ruff check
|
|
35
|
+
uv run ruff format --check
|
|
36
|
+
|
|
37
|
+
# The npm package is a port of the Python one and is tested against files generated
|
|
38
|
+
# from it. A Python change that forgets to regenerate them fails here, not in the port.
|
|
39
|
+
- name: Golden files are current
|
|
40
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
|
|
41
|
+
run: uv run python scripts/export_golden.py --check
|
|
42
|
+
|
|
43
|
+
node:
|
|
44
|
+
name: node (${{ matrix.os }}, Node ${{ matrix.node }})
|
|
45
|
+
runs-on: ${{ matrix.os }}
|
|
46
|
+
strategy:
|
|
47
|
+
fail-fast: false
|
|
48
|
+
matrix:
|
|
49
|
+
os: [ubuntu-latest, windows-latest]
|
|
50
|
+
# The oldest Node that package.json promises, and the current LTS.
|
|
51
|
+
node: ["18", "24"]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v7
|
|
54
|
+
|
|
55
|
+
- uses: actions/setup-node@v7
|
|
56
|
+
with:
|
|
57
|
+
node-version: ${{ matrix.node }}
|
|
58
|
+
|
|
59
|
+
- name: Parity tests against the Python reference
|
|
60
|
+
run: npm test
|
|
61
|
+
|
|
62
|
+
- name: The packed tarball runs
|
|
63
|
+
shell: bash
|
|
64
|
+
run: |
|
|
65
|
+
npm pack --pack-destination "$RUNNER_TEMP"
|
|
66
|
+
cd "$RUNNER_TEMP"
|
|
67
|
+
npx --yes ./rithik-*.tgz --version
|
|
68
|
+
npx --yes ./rithik-*.tgz scam "Your SBI KYC expires today, update at sbi-kyc.top"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Trusted publishing to both registries: PyPI and npm each accept uploads from this workflow,
|
|
4
|
+
# so no API token is stored anywhere. See docs/RELEASING.md.
|
|
5
|
+
on:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
|
|
18
|
+
# setup-uv stopped publishing floating major tags at v8, so this has to name a full
|
|
19
|
+
# release.
|
|
20
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
21
|
+
|
|
22
|
+
# Neither registry ever accepts a version twice, so a tag that disagrees with the code
|
|
23
|
+
# should stop here rather than upload something under the wrong number.
|
|
24
|
+
- name: Check that the tag matches every version string
|
|
25
|
+
run: |
|
|
26
|
+
version="${GITHUB_REF_NAME#v}"
|
|
27
|
+
test "$version" = "$(uv version --short)"
|
|
28
|
+
grep -qx "__version__ = \"$version\"" src/rithik/__init__.py
|
|
29
|
+
test "$version" = "$(node -p 'require("./package.json").version')"
|
|
30
|
+
|
|
31
|
+
- name: Build
|
|
32
|
+
run: uv build
|
|
33
|
+
|
|
34
|
+
- uses: actions/upload-artifact@v7
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
if-no-files-found: error
|
|
39
|
+
|
|
40
|
+
pypi:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
name: pypi
|
|
45
|
+
url: https://pypi.org/project/rithik/
|
|
46
|
+
# Only the upload job can mint the OIDC token that PyPI exchanges for upload rights.
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/download-artifact@v8
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
|
|
57
|
+
npm:
|
|
58
|
+
# Gated on the same checks as PyPI, so the two registries never disagree about a version.
|
|
59
|
+
needs: build
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v7
|
|
65
|
+
|
|
66
|
+
- uses: actions/setup-node@v7
|
|
67
|
+
with:
|
|
68
|
+
node-version: "24"
|
|
69
|
+
registry-url: "https://registry.npmjs.org"
|
|
70
|
+
# A release build should never restore anything from a cache.
|
|
71
|
+
package-manager-cache: false
|
|
72
|
+
|
|
73
|
+
# Trusted publishing needs npm 11.5.1 or later, which not every Node 24 build bundles.
|
|
74
|
+
- run: npm install -g npm@latest
|
|
75
|
+
|
|
76
|
+
- run: npm test
|
|
77
|
+
|
|
78
|
+
# With trusted publishing, npm exchanges the OIDC token itself and attaches a
|
|
79
|
+
# provenance attestation; there is no NODE_AUTH_TOKEN.
|
|
80
|
+
- run: npm publish
|
rithik-0.1.1/.gitignore
ADDED
rithik-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rithik Krishna T
|
|
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.
|
rithik-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rithik
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Rithik Krishna T's command-line card, plus an offline checker for Indian scam messages and links.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Daemon-VI/rithik
|
|
6
|
+
Project-URL: Issues, https://github.com/Daemon-VI/rithik/issues
|
|
7
|
+
Author-email: Rithik Krishna T <trithikkrishna@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,india,phishing,scam,security,sms,upi
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Communications
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# rithik
|
|
22
|
+
|
|
23
|
+
[](https://github.com/Daemon-VI/rithik/actions/workflows/ci.yml)
|
|
24
|
+
|
|
25
|
+
`rithik` prints my card in your terminal, and it checks suspicious messages and links for the
|
|
26
|
+
scam patterns common in India, entirely offline. Paste in an SMS, a WhatsApp forward or a link,
|
|
27
|
+
and it tells you which known patterns matched, why they matter, and what to do next.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
With Node.js 18 or newer, you can run it without installing anything:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx rithik # once it is published to npm
|
|
35
|
+
npx github:Daemon-VI/rithik # works now, straight from GitHub
|
|
36
|
+
npm install -g rithik # or keep the `rithik` command around
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
npm 12 refuses packages fetched from git unless you allow it, so on npm 12 the GitHub form is
|
|
40
|
+
`npx --allow-git=root github:Daemon-VI/rithik`. The npm 10 and 11 releases that ship with
|
|
41
|
+
Node 18 to 24 need no flag.
|
|
42
|
+
|
|
43
|
+
With Python 3.9 or newer:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pipx install git+https://github.com/Daemon-VI/rithik # works now
|
|
47
|
+
pipx install rithik # once it is published to PyPI
|
|
48
|
+
uvx rithik # run once without installing
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Neither version has any dependencies. If the `rithik` script is not on your `PATH`,
|
|
52
|
+
`python -m rithik` does the same thing.
|
|
53
|
+
|
|
54
|
+
### One engine, two runtimes
|
|
55
|
+
|
|
56
|
+
The Python package is the reference implementation, and the npm package is a JavaScript port
|
|
57
|
+
of it. The port is tested against golden files generated from the Python code: every corpus
|
|
58
|
+
message and edge case must produce the same verdict, reasons and score, and the terminal output
|
|
59
|
+
must match byte for byte. CI fails if the Python code changes and the golden files are not
|
|
60
|
+
regenerated.
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
rithik # the card
|
|
66
|
+
rithik card --json # the card as JSON
|
|
67
|
+
rithik scam "<message or link>"
|
|
68
|
+
rithik scam - # read the message from standard input
|
|
69
|
+
rithik --version
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`--no-color` turns colour off, and so does the `NO_COLOR` environment variable. Colour is also
|
|
73
|
+
off whenever the output is not a terminal. `--json` prints only JSON, so scripts can use it.
|
|
74
|
+
|
|
75
|
+
### Checking a message
|
|
76
|
+
|
|
77
|
+
The quotes matter, because they keep your shell from interpreting the message.
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
rithik scam "Dear customer, your SBI account will be blocked today. Update KYC now: https://sbi-kyc-help-desk.top/update"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The output (real, from v0.1.0):
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
LIKELY SCAM (score 0.96 of 1.00)
|
|
87
|
+
|
|
88
|
+
Why
|
|
89
|
+
- Link uses a bank or brand name on a domain that brand does not own:
|
|
90
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
91
|
+
- Asks you to update or verify KYC, PAN or Aadhaar through a message: "KYC
|
|
92
|
+
now: hxxps://sbi-kyc-help-desk[.]top/update"
|
|
93
|
+
- Claims your bank account, card or UPI will be blocked unless you act:
|
|
94
|
+
"account will be blocked"
|
|
95
|
+
- Link uses a cheap domain ending that is common in phishing:
|
|
96
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
97
|
+
- Threatens a loss or penalty to rush you: "will be blocked"
|
|
98
|
+
- Asks for something and gives a link that is not an official site:
|
|
99
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
100
|
+
|
|
101
|
+
Links in the message
|
|
102
|
+
- hxxps://sbi-kyc-help-desk[.]top/update
|
|
103
|
+
(dots shown as [.] so the links cannot be opened by accident)
|
|
104
|
+
|
|
105
|
+
What to do
|
|
106
|
+
- Do not click the links, call the numbers or install anything the message
|
|
107
|
+
asks for.
|
|
108
|
+
- Never share an OTP, UPI PIN or card details, and remember that you never
|
|
109
|
+
need your UPI PIN to receive money.
|
|
110
|
+
- Verify through your bank's official app, or the number printed on your
|
|
111
|
+
card, not a number from the message.
|
|
112
|
+
- If money was taken, call 1930 at once or report it at
|
|
113
|
+
https://cybercrime.gov.in
|
|
114
|
+
- Report the fraud SMS or call through Chakshu at
|
|
115
|
+
https://sancharsaathi.gov.in
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The verdict is one of `LIKELY SCAM`, `SUSPICIOUS` or `NO KNOWN SCAM SIGNALS`. The last one is
|
|
119
|
+
worded that way on purpose, because it only means that none of the known patterns matched:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
NO KNOWN SCAM SIGNALS (score 0.00 of 1.00)
|
|
123
|
+
|
|
124
|
+
None of the scam patterns this checker knows matched. That is not a guarantee
|
|
125
|
+
that the message is genuine: new scams appear all the time. Before you pay, or
|
|
126
|
+
share an OTP or PIN, verify through the official app or website.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A long message is easier to pipe in:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
pbpaste | rithik scam - # macOS
|
|
133
|
+
Get-Clipboard | rithik scam - # PowerShell
|
|
134
|
+
rithik scam - < message.txt
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
With `--json`, the report is printed as JSON, and links appear as the message wrote them.
|
|
138
|
+
This is the same check, trimmed to its first reason:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"verdict": "scam",
|
|
143
|
+
"score": 0.964,
|
|
144
|
+
"reasons": [
|
|
145
|
+
{
|
|
146
|
+
"code": "url_brand_impersonation",
|
|
147
|
+
"label": "Link uses a bank or brand name on a domain that brand does not own.",
|
|
148
|
+
"evidence": "https://sbi-kyc-help-desk.top/update",
|
|
149
|
+
"weight": 2.5
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
"urls": [
|
|
153
|
+
"https://sbi-kyc-help-desk.top/update"
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The exit code is 0 whenever a check ran, whatever the verdict. It is 2 for a usage error, such
|
|
159
|
+
as a missing or empty message.
|
|
160
|
+
|
|
161
|
+
## What the checker looks for
|
|
162
|
+
|
|
163
|
+
The message text is checked for these families of signals:
|
|
164
|
+
|
|
165
|
+
- urgency and threats
|
|
166
|
+
- KYC, PAN and Aadhaar update requests
|
|
167
|
+
- blocked or suspended accounts
|
|
168
|
+
- electricity disconnection notices
|
|
169
|
+
- UPI PIN requests and UPI collect requests
|
|
170
|
+
- requests for an OTP
|
|
171
|
+
- prize and lottery wins
|
|
172
|
+
- task-based job offers
|
|
173
|
+
- parcels held by a courier or at customs
|
|
174
|
+
- "digital arrest" threats and impersonation of officials
|
|
175
|
+
- remote-access apps and APK files to install
|
|
176
|
+
- loan and investment offers
|
|
177
|
+
- reward points, tax refunds and traffic e-challans
|
|
178
|
+
|
|
179
|
+
Every link is checked for:
|
|
180
|
+
|
|
181
|
+
- URL shorteners, which hide where a link goes
|
|
182
|
+
- bare IP addresses used in place of a domain name
|
|
183
|
+
- punycode (`xn--`) domains, which can imitate other scripts
|
|
184
|
+
- lookalike domains that use a bank's or brand's name but are not on its list of official domains
|
|
185
|
+
- domain endings (TLDs) that are common in scam campaigns
|
|
186
|
+
|
|
187
|
+
Each signal carries a weight, and together they make the score that decides the verdict. Each
|
|
188
|
+
reason in the output names the words or the link that triggered it, so you can judge it for
|
|
189
|
+
yourself.
|
|
190
|
+
|
|
191
|
+
## Accuracy
|
|
192
|
+
|
|
193
|
+
Measured results, and how the evaluation set was built, are in
|
|
194
|
+
[docs/EVAL.md](https://github.com/Daemon-VI/rithik/blob/main/docs/EVAL.md).
|
|
195
|
+
This README gives no figures, so that it cannot drift out of step with that file.
|
|
196
|
+
|
|
197
|
+
## Limitations
|
|
198
|
+
|
|
199
|
+
- It is a heuristic. It recognises patterns that someone wrote down, and it will miss new or
|
|
200
|
+
unusual scams.
|
|
201
|
+
- It understands English and Hinglish (Hindi written in the Latin alphabet) only.
|
|
202
|
+
- A result of `NO KNOWN SCAM SIGNALS` is not a guarantee that a message is safe.
|
|
203
|
+
- It never opens links, so it judges a link by its address alone, not by the page behind it.
|
|
204
|
+
- The evaluation set is synthetic: messages written from public fraud advisories rather than
|
|
205
|
+
collected from real people. Its scores describe that set, not real-world accuracy.
|
|
206
|
+
|
|
207
|
+
## Privacy
|
|
208
|
+
|
|
209
|
+
Everything runs on your machine. `rithik` makes no network requests, stores nothing, and
|
|
210
|
+
collects no telemetry. The message you check exists only in the running process.
|
|
211
|
+
|
|
212
|
+
## Where to report fraud in India
|
|
213
|
+
|
|
214
|
+
- **Lost money?** Call the National Cyber Crime Helpline, **1930**, as soon as possible, or
|
|
215
|
+
file a complaint at <https://cybercrime.gov.in>.
|
|
216
|
+
- **Fraud calls, SMS or WhatsApp messages:** report them through Chakshu at
|
|
217
|
+
<https://sancharsaathi.gov.in>.
|
|
218
|
+
- Contact your bank directly, using the number printed on your card or the bank's official
|
|
219
|
+
app.
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
To catch a new pattern, add a rule together with a unit test that shows it firing on a
|
|
224
|
+
message it should catch and staying quiet on a similar genuine one.
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
uv sync
|
|
228
|
+
uv run pytest -q
|
|
229
|
+
uv run ruff check
|
|
230
|
+
uv run ruff format
|
|
231
|
+
uv run python scripts/export_golden.py # after any behaviour change
|
|
232
|
+
npm test # the JavaScript port must still match
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
A rule change goes into both `src/rithik/scam/` and its mirror in `js/lib/scam/`. Change the
|
|
236
|
+
Python first, regenerate the golden files, then bring the port level until `npm test` passes.
|
|
237
|
+
|
|
238
|
+
Some constraints apply:
|
|
239
|
+
|
|
240
|
+
- The code must run on Python 3.9 and Node 18, and use only the standard library.
|
|
241
|
+
- It must never touch the network.
|
|
242
|
+
- Test messages must be invented. Never use a real person's message, number or account.
|
|
243
|
+
- Scam links in tests must use invented domains.
|
|
244
|
+
|
|
245
|
+
Releases are covered in [docs/RELEASING.md](https://github.com/Daemon-VI/rithik/blob/main/docs/RELEASING.md).
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT. See [LICENSE](https://github.com/Daemon-VI/rithik/blob/main/LICENSE).
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
Made by [Rithik Krishna T](https://github.com/Daemon-VI).
|
rithik-0.1.1/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# rithik
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Daemon-VI/rithik/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
`rithik` prints my card in your terminal, and it checks suspicious messages and links for the
|
|
6
|
+
scam patterns common in India, entirely offline. Paste in an SMS, a WhatsApp forward or a link,
|
|
7
|
+
and it tells you which known patterns matched, why they matter, and what to do next.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
With Node.js 18 or newer, you can run it without installing anything:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx rithik # once it is published to npm
|
|
15
|
+
npx github:Daemon-VI/rithik # works now, straight from GitHub
|
|
16
|
+
npm install -g rithik # or keep the `rithik` command around
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
npm 12 refuses packages fetched from git unless you allow it, so on npm 12 the GitHub form is
|
|
20
|
+
`npx --allow-git=root github:Daemon-VI/rithik`. The npm 10 and 11 releases that ship with
|
|
21
|
+
Node 18 to 24 need no flag.
|
|
22
|
+
|
|
23
|
+
With Python 3.9 or newer:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pipx install git+https://github.com/Daemon-VI/rithik # works now
|
|
27
|
+
pipx install rithik # once it is published to PyPI
|
|
28
|
+
uvx rithik # run once without installing
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Neither version has any dependencies. If the `rithik` script is not on your `PATH`,
|
|
32
|
+
`python -m rithik` does the same thing.
|
|
33
|
+
|
|
34
|
+
### One engine, two runtimes
|
|
35
|
+
|
|
36
|
+
The Python package is the reference implementation, and the npm package is a JavaScript port
|
|
37
|
+
of it. The port is tested against golden files generated from the Python code: every corpus
|
|
38
|
+
message and edge case must produce the same verdict, reasons and score, and the terminal output
|
|
39
|
+
must match byte for byte. CI fails if the Python code changes and the golden files are not
|
|
40
|
+
regenerated.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
rithik # the card
|
|
46
|
+
rithik card --json # the card as JSON
|
|
47
|
+
rithik scam "<message or link>"
|
|
48
|
+
rithik scam - # read the message from standard input
|
|
49
|
+
rithik --version
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--no-color` turns colour off, and so does the `NO_COLOR` environment variable. Colour is also
|
|
53
|
+
off whenever the output is not a terminal. `--json` prints only JSON, so scripts can use it.
|
|
54
|
+
|
|
55
|
+
### Checking a message
|
|
56
|
+
|
|
57
|
+
The quotes matter, because they keep your shell from interpreting the message.
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
rithik scam "Dear customer, your SBI account will be blocked today. Update KYC now: https://sbi-kyc-help-desk.top/update"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The output (real, from v0.1.0):
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
LIKELY SCAM (score 0.96 of 1.00)
|
|
67
|
+
|
|
68
|
+
Why
|
|
69
|
+
- Link uses a bank or brand name on a domain that brand does not own:
|
|
70
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
71
|
+
- Asks you to update or verify KYC, PAN or Aadhaar through a message: "KYC
|
|
72
|
+
now: hxxps://sbi-kyc-help-desk[.]top/update"
|
|
73
|
+
- Claims your bank account, card or UPI will be blocked unless you act:
|
|
74
|
+
"account will be blocked"
|
|
75
|
+
- Link uses a cheap domain ending that is common in phishing:
|
|
76
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
77
|
+
- Threatens a loss or penalty to rush you: "will be blocked"
|
|
78
|
+
- Asks for something and gives a link that is not an official site:
|
|
79
|
+
"hxxps://sbi-kyc-help-desk[.]top/update"
|
|
80
|
+
|
|
81
|
+
Links in the message
|
|
82
|
+
- hxxps://sbi-kyc-help-desk[.]top/update
|
|
83
|
+
(dots shown as [.] so the links cannot be opened by accident)
|
|
84
|
+
|
|
85
|
+
What to do
|
|
86
|
+
- Do not click the links, call the numbers or install anything the message
|
|
87
|
+
asks for.
|
|
88
|
+
- Never share an OTP, UPI PIN or card details, and remember that you never
|
|
89
|
+
need your UPI PIN to receive money.
|
|
90
|
+
- Verify through your bank's official app, or the number printed on your
|
|
91
|
+
card, not a number from the message.
|
|
92
|
+
- If money was taken, call 1930 at once or report it at
|
|
93
|
+
https://cybercrime.gov.in
|
|
94
|
+
- Report the fraud SMS or call through Chakshu at
|
|
95
|
+
https://sancharsaathi.gov.in
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The verdict is one of `LIKELY SCAM`, `SUSPICIOUS` or `NO KNOWN SCAM SIGNALS`. The last one is
|
|
99
|
+
worded that way on purpose, because it only means that none of the known patterns matched:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
NO KNOWN SCAM SIGNALS (score 0.00 of 1.00)
|
|
103
|
+
|
|
104
|
+
None of the scam patterns this checker knows matched. That is not a guarantee
|
|
105
|
+
that the message is genuine: new scams appear all the time. Before you pay, or
|
|
106
|
+
share an OTP or PIN, verify through the official app or website.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
A long message is easier to pipe in:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
pbpaste | rithik scam - # macOS
|
|
113
|
+
Get-Clipboard | rithik scam - # PowerShell
|
|
114
|
+
rithik scam - < message.txt
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
With `--json`, the report is printed as JSON, and links appear as the message wrote them.
|
|
118
|
+
This is the same check, trimmed to its first reason:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"verdict": "scam",
|
|
123
|
+
"score": 0.964,
|
|
124
|
+
"reasons": [
|
|
125
|
+
{
|
|
126
|
+
"code": "url_brand_impersonation",
|
|
127
|
+
"label": "Link uses a bank or brand name on a domain that brand does not own.",
|
|
128
|
+
"evidence": "https://sbi-kyc-help-desk.top/update",
|
|
129
|
+
"weight": 2.5
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"urls": [
|
|
133
|
+
"https://sbi-kyc-help-desk.top/update"
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The exit code is 0 whenever a check ran, whatever the verdict. It is 2 for a usage error, such
|
|
139
|
+
as a missing or empty message.
|
|
140
|
+
|
|
141
|
+
## What the checker looks for
|
|
142
|
+
|
|
143
|
+
The message text is checked for these families of signals:
|
|
144
|
+
|
|
145
|
+
- urgency and threats
|
|
146
|
+
- KYC, PAN and Aadhaar update requests
|
|
147
|
+
- blocked or suspended accounts
|
|
148
|
+
- electricity disconnection notices
|
|
149
|
+
- UPI PIN requests and UPI collect requests
|
|
150
|
+
- requests for an OTP
|
|
151
|
+
- prize and lottery wins
|
|
152
|
+
- task-based job offers
|
|
153
|
+
- parcels held by a courier or at customs
|
|
154
|
+
- "digital arrest" threats and impersonation of officials
|
|
155
|
+
- remote-access apps and APK files to install
|
|
156
|
+
- loan and investment offers
|
|
157
|
+
- reward points, tax refunds and traffic e-challans
|
|
158
|
+
|
|
159
|
+
Every link is checked for:
|
|
160
|
+
|
|
161
|
+
- URL shorteners, which hide where a link goes
|
|
162
|
+
- bare IP addresses used in place of a domain name
|
|
163
|
+
- punycode (`xn--`) domains, which can imitate other scripts
|
|
164
|
+
- lookalike domains that use a bank's or brand's name but are not on its list of official domains
|
|
165
|
+
- domain endings (TLDs) that are common in scam campaigns
|
|
166
|
+
|
|
167
|
+
Each signal carries a weight, and together they make the score that decides the verdict. Each
|
|
168
|
+
reason in the output names the words or the link that triggered it, so you can judge it for
|
|
169
|
+
yourself.
|
|
170
|
+
|
|
171
|
+
## Accuracy
|
|
172
|
+
|
|
173
|
+
Measured results, and how the evaluation set was built, are in
|
|
174
|
+
[docs/EVAL.md](https://github.com/Daemon-VI/rithik/blob/main/docs/EVAL.md).
|
|
175
|
+
This README gives no figures, so that it cannot drift out of step with that file.
|
|
176
|
+
|
|
177
|
+
## Limitations
|
|
178
|
+
|
|
179
|
+
- It is a heuristic. It recognises patterns that someone wrote down, and it will miss new or
|
|
180
|
+
unusual scams.
|
|
181
|
+
- It understands English and Hinglish (Hindi written in the Latin alphabet) only.
|
|
182
|
+
- A result of `NO KNOWN SCAM SIGNALS` is not a guarantee that a message is safe.
|
|
183
|
+
- It never opens links, so it judges a link by its address alone, not by the page behind it.
|
|
184
|
+
- The evaluation set is synthetic: messages written from public fraud advisories rather than
|
|
185
|
+
collected from real people. Its scores describe that set, not real-world accuracy.
|
|
186
|
+
|
|
187
|
+
## Privacy
|
|
188
|
+
|
|
189
|
+
Everything runs on your machine. `rithik` makes no network requests, stores nothing, and
|
|
190
|
+
collects no telemetry. The message you check exists only in the running process.
|
|
191
|
+
|
|
192
|
+
## Where to report fraud in India
|
|
193
|
+
|
|
194
|
+
- **Lost money?** Call the National Cyber Crime Helpline, **1930**, as soon as possible, or
|
|
195
|
+
file a complaint at <https://cybercrime.gov.in>.
|
|
196
|
+
- **Fraud calls, SMS or WhatsApp messages:** report them through Chakshu at
|
|
197
|
+
<https://sancharsaathi.gov.in>.
|
|
198
|
+
- Contact your bank directly, using the number printed on your card or the bank's official
|
|
199
|
+
app.
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
To catch a new pattern, add a rule together with a unit test that shows it firing on a
|
|
204
|
+
message it should catch and staying quiet on a similar genuine one.
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
uv sync
|
|
208
|
+
uv run pytest -q
|
|
209
|
+
uv run ruff check
|
|
210
|
+
uv run ruff format
|
|
211
|
+
uv run python scripts/export_golden.py # after any behaviour change
|
|
212
|
+
npm test # the JavaScript port must still match
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
A rule change goes into both `src/rithik/scam/` and its mirror in `js/lib/scam/`. Change the
|
|
216
|
+
Python first, regenerate the golden files, then bring the port level until `npm test` passes.
|
|
217
|
+
|
|
218
|
+
Some constraints apply:
|
|
219
|
+
|
|
220
|
+
- The code must run on Python 3.9 and Node 18, and use only the standard library.
|
|
221
|
+
- It must never touch the network.
|
|
222
|
+
- Test messages must be invented. Never use a real person's message, number or account.
|
|
223
|
+
- Scam links in tests must use invented domains.
|
|
224
|
+
|
|
225
|
+
Releases are covered in [docs/RELEASING.md](https://github.com/Daemon-VI/rithik/blob/main/docs/RELEASING.md).
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT. See [LICENSE](https://github.com/Daemon-VI/rithik/blob/main/LICENSE).
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
Made by [Rithik Krishna T](https://github.com/Daemon-VI).
|