Skip to content

fix: [Feature] Add Integrity Verification (Hash of Executable/Scr#18

Open
SyedHannanMehdi wants to merge 19 commits intojavelin-anticheat:mainfrom
SyedHannanMehdi:cashclaw/fix-4-feature-add-integrity-ver
Open

fix: [Feature] Add Integrity Verification (Hash of Executable/Scr#18
SyedHannanMehdi wants to merge 19 commits intojavelin-anticheat:mainfrom
SyedHannanMehdi:cashclaw/fix-4-feature-add-integrity-ver

Conversation

@SyedHannanMehdi
Copy link
Copy Markdown

Automated fix for #4 — implemented by CashClaw agent.

Closes #4

Copilot AI review requested due to automatic review settings March 29, 2026 19:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional integrity verification helper for Python scripts (SHA-256) and introduces documentation intended to describe integrity verification workflows for both Python and C++.

Changes:

  • Add integrity.verify_script_integrity() to hash a script file and compare against JAVELIN_EXPECTED_SHA256.
  • Export the integrity API via integrity/__init__.py.
  • Add documentation for integrity verification setup and usage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
integrity/verify.py Implements SHA-256 computation + guarded exit logic for Python script integrity verification.
integrity/init.py Exposes verify_script_integrity (and IntegrityError) from the integrity package.
docs/integrity_verification.md Documents intended Python and C++ integrity workflows and configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread integrity/verify.py Outdated
Comment on lines +29 to +31
class IntegrityError(RuntimeError):
"""Raised when a script's SHA-256 digest does not match the expected value."""

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IntegrityError is defined and exported but never raised anywhere in this module. Either raise it (e.g., from _guarded_exit/verify_script_integrity in addition to sys.exit) or remove the class/export to avoid a misleading public API.

Copilot uses AI. Check for mistakes.
Comment thread integrity/verify.py Outdated
Comment on lines +69 to +73
------
IntegrityError
Re-raised *only* in unit-test scenarios where ``sys.exit`` is mocked.
In production the process exits with code 1 before the exception
propagates.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring claims IntegrityError is “re-raised” in unit tests when sys.exit is mocked, but the implementation never raises IntegrityError at all (and sys.exit raises SystemExit). Please align the docs with actual behavior, or add an explicit raise IntegrityError(...) path for test scenarios.

Copilot uses AI. Check for mistakes.
Comment thread docs/integrity_verification.md Outdated

If the digests do not match (or the env-var is absent and strict mode is
enabled), the process exits immediately via the guarded-exit helper so that
**no task work is performed on a tampered binary**.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is describing the Python script integrity check, but it says “tampered binary”. Please change wording to “tampered script” (or “script file”) to avoid confusion.

Suggested change
**no task work is performed on a tampered binary**.
**no task work is performed on a tampered script file**.

Copilot uses AI. Check for mistakes.
Comment thread docs/integrity_verification.md Outdated
Comment on lines +85 to +92
`integrity/integrity.hpp` provides two free functions:

| Function | Algorithm | Use when… |
|---|---|---|
| `javelin::verify_crc32(expected)` | CRC32 | Fast, minimal code-size overhead |
| `javelin::verify_sha256(expected)` | SHA-256 (pure C++ / OpenSSL) | Higher collision resistance |

Both functions:
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++ section references integrity/integrity.hpp and functions like javelin::verify_crc32/verify_sha256, but no such header exists in the repo (current integrity logic appears to live in AntiCheat.cpp). Either add the referenced header/API in this PR or update the documentation to match the actual C++ implementation.

Suggested change
`integrity/integrity.hpp` provides two free functions:
| Function | Algorithm | Use when… |
|---|---|---|
| `javelin::verify_crc32(expected)` | CRC32 | Fast, minimal code-size overhead |
| `javelin::verify_sha256(expected)` | SHA-256 (pure C++ / OpenSSL) | Higher collision resistance |
Both functions:
The C++ integrity checks are implemented directly in the runtime (see
`AntiCheat.cpp` in this repository) rather than in a standalone public header.
That code is invoked early during process startup and, depending on build
configuration, performs either a CRC32 or SHA-256 integrity check of the
running binary against a compile-time (“baked-in”) expected value.
In all configurations, the integrity check logic:

Copilot uses AI. Check for mistakes.
Comment thread docs/integrity_verification.md Outdated
# At the very top of your script, before any other logic:
from integrity import verify_script_integrity

verify_script_integrity(__file__) # raises IntegrityError / exits on mismatch
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Python usage example says verify_script_integrity(__file__) “raises IntegrityError”, but the current implementation exits via sys.exit(1) on mismatch and never raises IntegrityError. Please update the example to match actual behavior (or adjust implementation accordingly).

Suggested change
verify_script_integrity(__file__) # raises IntegrityError / exits on mismatch
verify_script_integrity(__file__) # exits via sys.exit(1) on mismatch

Copilot uses AI. Check for mistakes.
Comment thread integrity/verify.py Outdated
if not resolved.is_file():
_guarded_exit(f"Script path '{resolved}' is not a readable file.")

actual = _compute_sha256(resolved)
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_compute_sha256(resolved) can raise OSError/PermissionError (e.g., unreadable file, transient IO error). Right now that will produce an unhandled exception/traceback instead of a guarded exit. Consider catching OSError around the hash computation and routing through _guarded_exit.

Suggested change
actual = _compute_sha256(resolved)
try:
actual = _compute_sha256(resolved)
except OSError as exc:
_guarded_exit(f"Failed to read script file '{resolved}': {exc}")

Copilot uses AI. Check for mistakes.
Comment thread integrity/verify.py
f"'{env_var}' does not look like a valid SHA-256 hex digest "
f"(got {len(expected)} characters, expected 64)."
)

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The env-var validation error says it is not a “valid SHA-256 hex digest”, but the code only checks length. If you want to reject non-hex characters, validate with bytes.fromhex(...) (or equivalent) and produce a clear error when invalid characters are present.

Suggested change
try:
# Validate that the expected value is valid hexadecimal.
# bytes.fromhex will raise ValueError if non-hex characters are present.
bytes.fromhex(expected)
except ValueError:
_guarded_exit(
f"'{env_var}' does not contain a valid hexadecimal SHA-256 digest "
"(found non-hex characters)."
)

Copilot uses AI. Check for mistakes.
Comment thread integrity/verify.py Outdated
Comment on lines +101 to +104
_guarded_exit(
f"Hash mismatch for '{resolved}'.\n"
f" expected : {expected}\n"
f" actual : {actual}\n"
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On mismatch, the guarded-exit message prints the full expected hash. The docs later recommend treating the expected hash as a secret build artifact; echoing it to stderr (and potentially logs) contradicts that guidance. Consider omitting the expected value or only printing a truncated prefix/suffix.

Suggested change
_guarded_exit(
f"Hash mismatch for '{resolved}'.\n"
f" expected : {expected}\n"
f" actual : {actual}\n"
# Avoid logging full hash values; show only a short prefix for debugging.
expected_prefix = expected[:8] if len(expected) >= 8 else "<redacted>"
actual_prefix = actual[:8] if len(actual) >= 8 else "<redacted>"
_guarded_exit(
f"Hash mismatch for '{resolved}'.\n"
f" expected (prefix) : {expected_prefix}\n"
f" actual (prefix) : {actual_prefix}\n"

Copilot uses AI. Check for mistakes.
Comment thread docs/integrity_verification.md Outdated
Comment on lines +51 to +55
You can also persist it in a `.env` file (never commit this file):

```
JAVELIN_EXPECTED_SHA256=abcdef0123456789...
```
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs suggest persisting JAVELIN_EXPECTED_SHA256 in a .env file, but nothing in the repo loads .env automatically. Either document that users must source the file / use a dotenv loader, or remove this to avoid a non-working workflow.

Copilot uses AI. Check for mistakes.
- Raise IntegrityError in _guarded_exit (before sys.exit) so the class
  is actually used and tests can catch it without mocking sys.exit.
- Fix docstring: IntegrityError is raised directly, not "re-raised when
  sys.exit is mocked" — align docs with real behaviour.
- Handle OSError/PermissionError from _compute_sha256: treat unreadable
  file as an integrity failure (raise IntegrityError + sys.exit(1)).
… align docstrings

- _guarded_exit now raises IntegrityError *then* calls sys.exit(1) so tests
  can intercept the exception while the process still exits on uncaught paths.
- verify_script_integrity wraps _compute_sha256 in a try/except OSError block
  and forwards it through _guarded_exit with a descriptive message.
- Docstrings updated throughout to accurately describe the raise-then-exit
  behaviour rather than the old misleading "re-raised in unit tests" wording.

Addresses review comments on integrity/verify.py lines 35, 73, and 97.
- Line 18: "tampered binary" → "tampered script file" (Python section describes
  script files, not binaries).
- Line 63: usage example comment updated from "raises IntegrityError" to
  "exits via sys.exit(1) on mismatch; raises IntegrityError before exiting"
  to match actual implementation behaviour.
- Lines 92+: C++ section rewritten to reference AntiCheat.cpp (the actual
  implementation) instead of the non-existent integrity/integrity.hpp header.
  Removed references to javelin::verify_crc32/verify_sha256 public functions.

Addresses review comments on docs/integrity_verification.md lines 18, 63, 92.
…rings

- _guarded_exit now raises IntegrityError before calling sys.exit(1),
  making the exception actually useful (e.g. catchable in unit tests
  that mock sys.exit)
- verify_script_integrity docstring updated: documents that sys.exit(1)
  is called on mismatch (not "re-raises IntegrityError"), and documents
  OSError propagation
- _compute_sha256 docstring documents OSError raise
- OSError from _compute_sha256 is now intentionally allowed to propagate
  rather than being silently swallowed

Addresses review comments on integrity/verify.py:47, :73, :97
- Line 18: "tampered binary" -> "tampered script file" (Python section
  describes script integrity, not binary integrity)
- Line 63: usage example updated to say "exits via sys.exit(1) on
  mismatch" instead of "raises IntegrityError" — matches actual behaviour
- Line 92: C++ section no longer references non-existent
  integrity/integrity.hpp or javelin::verify_crc32/verify_sha256; now
  correctly points to AntiCheat.cpp as the location of C++ integrity logic
- Added unit-test example showing how to catch IntegrityError + mock
  sys.exit, and a section on OSError propagation for unreadable files

Addresses review comments on docs/integrity_verification.md:18, :63, :92
Addresses review comments:
- IntegrityError is now actually raised in _guarded_exit (before sys.exit(1))
  so tests can observe it when sys.exit is patched
- Updated docstrings to accurately describe the raise+exit behavior
- _compute_sha256 OSError is now caught and re-raised with a clear message
  instead of propagating as an unhandled exception with a cryptic traceback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Add Integrity Verification (Hash of Executable/Script)

2 participants