Skip to content

Commit 4517a4a

Browse files
Initial commit: CI3 request analysis hook
0 parents  commit 4517a4a

12 files changed

Lines changed: 1002 additions & 0 deletions

File tree

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.env
2+
.env.*
3+
!.env.example
4+
/vendor/
5+
/composer.lock
6+
/sample/application/logs/
7+
/sample/application/uploads/
8+
*.log
9+
*.gz

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [1.0.0] - 2026-09-02
9+
10+
### Added
11+
12+
- Initial release: CI3 `post_controller_constructor` hook
13+
`RequestLogHook` that captures request metadata — headers, raw body, file
14+
uploads, client IP, user agent, query string — and writes one JSONL line
15+
per request to `application/logs/analysis.log`.
16+
- `RequestLogService` with sensitive-field redaction (configurable via
17+
`REQUEST_LOG_REDACT_FIELDS`), body truncation (3 MB default), file metadata
18+
extraction (name, size, MIME, SHA-256 hash, double-extension detection), and
19+
IP/CIDR whitelist.
20+
- `RequestLog` config class that reads `REQUEST_LOG_*` environment variables
21+
with sensible defaults.
22+
- Daily log rotation with gzip compression and retention pruning
23+
(`REQUEST_LOG_RETENTION_DAYS`, default 30).
24+
- Sample CI3 application under `sample/` with a Home controller demonstrating
25+
JSON POST and multipart file upload endpoints.
26+
27+
[1.0.0]: https://github.com/mrnaeem4/ci3-request-analysis/releases/tag/v1.0.0

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 mrnaeem4
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.

README.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# ci3-request-analysis
2+
3+
CodeIgniter 3 hook that intercepts incoming HTTP requests, extracts structured
4+
metadata (headers, body, files, tenant, source IP, etc.), and writes one JSON
5+
Line per request directly to the CI3 `application/logs` directory — with
6+
sensitive-field redaction, body truncation, IP whitelisting, and daily log
7+
rotation with gzip compression.
8+
9+
> Local logging only. No external server, no queue, no Guzzle. This is the
10+
> CI3 counterpart of [ci4-request-analysis](https://github.com/mrnaeem4/ci4-request-analysis).
11+
12+
## Features
13+
14+
- Attachable per-request via a single CI3 hook (`post_controller_constructor`),
15+
not a global middleware.
16+
- Writes directly to `application/logs/analysis.log` as JSON Lines (JSONL).
17+
- Daily rotation: a file from a previous day is renamed and gzip-compressed
18+
automatically on the next write.
19+
- Retention pruning: compressed logs older than `REQUEST_LOG_RETENTION_DAYS`
20+
(default 30) are deleted.
21+
- Configurable sensitive-field redaction (default: `password`, `nik`, `Api-Key`, `no_telp`).
22+
- Raw body truncation at 3 MB (configurable) with `... [truncated]` suffix.
23+
- File upload metadata captured without binary content (name, size, MIME,
24+
extension, SHA-256 hash, double-extension detection).
25+
- IP/CIDR whitelist to skip private/internal traffic.
26+
- PHP 7.4+.
27+
28+
## Requirements
29+
30+
- PHP 7.4+
31+
- CodeIgniter 3.x
32+
- Composer (for autoloading), with a writable `application/logs` directory
33+
34+
## Installation
35+
36+
```bash
37+
composer require mrnaeem4/ci3-request-analysis
38+
```
39+
40+
If you do not use Composer, add a PSR-4 autoloader mapping
41+
`MrNaeem\Ci3RequestAnalysis\` to the `src/` directory, or require the three
42+
classes manually:
43+
44+
```php
45+
require_once __DIR__ . '/vendor/mrnaeem4/ci3-request-analysis/src/Config/RequestLog.php';
46+
require_once __DIR__ . '/vendor/mrnaeem4/ci3-request-analysis/src/Services/RequestLogService.php';
47+
require_once __DIR__ . '/vendor/mrnaeem4/ci3-request-analysis/src/Hooks/RequestLogHook.php';
48+
```
49+
50+
## Configuration
51+
52+
### 1. Enable hooks + Composer autoload
53+
54+
In `application/config/config.php`:
55+
56+
```php
57+
$config['enable_hooks'] = TRUE;
58+
$config['composer_autoload'] = TRUE; // or absolute path to vendor/autoload.php
59+
```
60+
61+
### 2. Register the hook
62+
63+
In `application/config/hooks.php`:
64+
65+
```php
66+
$hook['post_controller_constructor'] = [
67+
'class' => 'MrNaeem\Ci3RequestAnalysis\Hooks\RequestLogHook',
68+
'function' => 'before',
69+
'filename' => '',
70+
'filepath' => '',
71+
'params' => [],
72+
];
73+
```
74+
75+
### 3. Set environment variables
76+
77+
Set in your web server environment or system environment:
78+
79+
```ini
80+
REQUEST_LOG_ENABLED = true
81+
REQUEST_LOG_DIR = "" # empty → application/logs
82+
REQUEST_LOG_FILE = "analysis.log"
83+
REQUEST_LOG_REDACT_FIELDS = "password,nik,Api-Key,no_telp"
84+
REQUEST_LOG_MAX_BODY_SIZE = 3145728
85+
REQUEST_LOG_WHITELIST_IPS = "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1"
86+
REQUEST_LOG_TRUNCATE_SUFFIX = "... [truncated]"
87+
REQUEST_LOG_RETENTION_DAYS = 30
88+
```
89+
90+
### Config reference
91+
92+
| Variable | Default | Description |
93+
|---|---|---|
94+
| `REQUEST_LOG_ENABLED` | `false` | Master switch for the hook. |
95+
| `REQUEST_LOG_DIR` | `''` (→ `application/logs`) | Directory for the log file. |
96+
| `REQUEST_LOG_FILE` | `analysis.log` | Log file name (single file, rotated daily). |
97+
| `REQUEST_LOG_REDACT_FIELDS` | `password,nik,Api-Key,no_telp` | Comma-separated sensitive fields (case-insensitive). |
98+
| `REQUEST_LOG_MAX_BODY_SIZE` | `3145728` (3 MB) | `raw_body` truncation length (bytes). |
99+
| `REQUEST_LOG_WHITELIST_IPS` | RFC1918 + localhost | CIDR ranges to skip. |
100+
| `REQUEST_LOG_TRUNCATE_SUFFIX` | `... [truncated]` | Appended when the body is truncated. |
101+
| `REQUEST_LOG_RETENTION_DAYS` | `30` | Days of compressed logs kept before pruning. |
102+
103+
## Log payload
104+
105+
Each line in `analysis.log` is a JSON object (envelope + `log_data`):
106+
107+
```json
108+
{
109+
"log_data": {
110+
"timestamp": "2026-08-31T02:15:04+00:00",
111+
"domain": "app.example.com",
112+
"path": "/api/profile/update",
113+
"method": "POST",
114+
"srcip": "203.0.113.10",
115+
"user_agent": "Mozilla/5.0 ...",
116+
"query_string": "page=1",
117+
"headers": { "Content-Type": "application/json", ... },
118+
"raw_body": "{\"name\":\"User\",\"email\":\"user@example.com\",\"password\":\"***REDACTED***\"}",
119+
"file_count": 1,
120+
"file_names": ["shell.php.jpg"],
121+
"file_metadata": [
122+
{
123+
"original_name": "shell.php.jpg",
124+
"size": 20480,
125+
"mime_type": "image/jpeg",
126+
"extension": "jpg",
127+
"hash": "3c98...",
128+
"has_double_extension": true
129+
}
130+
]
131+
},
132+
"retry_count": 0,
133+
"last_attempt": null,
134+
"created_at": "2026-08-31T02:15:04+00:00"
135+
}
136+
```
137+
138+
## How it works
139+
140+
```
141+
Request → CI3 Hook (post_controller_constructor)
142+
├─ enabled? ──no──► done
143+
├─ IP whitelisted? ──yes──► done
144+
├─ collect: headers, body, files, tenant, srcip...
145+
│ ├─ redact sensitive fields (headers + body)
146+
│ ├─ truncate body at max size
147+
│ └─ extract file metadata (no binary)
148+
├─ rotate if the active log is from a previous day (rename + gzip + prune)
149+
└─ append one JSONL line to application/logs/analysis.log
150+
```
151+
152+
The write is a single append with `LOCK_EX`, so it does not block the request
153+
and is safe for concurrent PHP-FPM workers.
154+
155+
## Notes
156+
157+
- The hook runs on every request once enabled. To log only specific
158+
controllers, guard inside your controller or move the hook logic into a
159+
base-controller `__construct()` call — see
160+
`sample/application/controllers/Home.php` for a self-contained demo.
161+
- Binary file content is never stored; only metadata is captured.
162+
- Redaction applies to both request headers (e.g. `Cookie`) and the body.
163+
- `application/logs` should not be publicly accessible.
164+
165+
## Sample app
166+
167+
A minimal CI3 application fragment lives under `sample/`:
168+
169+
- `sample/application/config/config.php` — hooks + Composer autoload enabled
170+
- `sample/application/config/hooks.php` — hook registration
171+
- `sample/application/controllers/Home.php``post` + `upload` demo endpoints
172+
- `sample/application/views/home/index.php` — test forms
173+
174+
Copy the relevant files into an existing CI3 application and start your server.
175+
176+
## Changelog
177+
178+
See [CHANGELOG.md](CHANGELOG.md).
179+
180+
## License
181+
182+
[MIT](LICENSE)

composer.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "mrnaeem4/ci3-request-analysis",
3+
"description": "CodeIgniter 3 hook that intercepts incoming HTTP requests and writes structured JSON request logs (JSONL) to the CI3 logs directory with daily rotation and gzip compression.",
4+
"type": "library",
5+
"license": "MIT",
6+
"keywords": [
7+
"codeigniter3",
8+
"request-log",
9+
"jsonl",
10+
"hook",
11+
"log-rotation"
12+
],
13+
"require": {
14+
"php": ">=7.4"
15+
},
16+
"autoload": {
17+
"psr-4": {
18+
"MrNaeem\\Ci3RequestAnalysis\\": "src/"
19+
}
20+
},
21+
"config": {
22+
"sort-packages": true
23+
},
24+
"support": {
25+
"issues": "https://github.com/mrnaeem4/ci3-request-analysis/issues"
26+
}
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
defined('BASEPATH') OR exit('No direct script access allowed');
3+
4+
/*
5+
|--------------------------------------------------------------------------
6+
| Composer Auto-Loading
7+
|--------------------------------------------------------------------------
8+
| Enabling this setting will tell CodeIgniter to look for a Composer
9+
| package installer script in `application/vendor/` (vendor/autoload.php)
10+
| or wherever you point it below.
11+
*/
12+
$config['composer_autoload'] = TRUE;
13+
14+
/*
15+
|--------------------------------------------------------------------------
16+
| Enable/Disable Hooks
17+
|--------------------------------------------------------------------------
18+
| Set this to TRUE to enable the hooks system.
19+
*/
20+
$config['enable_hooks'] = TRUE;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
defined('BASEPATH') OR exit('No direct script access allowed');
3+
4+
/*
5+
| -------------------------------------------------------------------------
6+
| Hooks
7+
| -------------------------------------------------------------------------
8+
| This file lets you define "hooks" to extend CI without hacking the core
9+
| files. Set $config['enable_hooks'] = TRUE in config.php first.
10+
|
11+
| Attach the request analysis hook to post_controller_constructor so it runs
12+
| after the controller is instantiated (Composer autoload is already loaded).
13+
*/
14+
15+
$hook['post_controller_constructor'] = [
16+
'class' => 'MrNaeem\Ci3RequestAnalysis\Hooks\RequestLogHook',
17+
'function' => 'before',
18+
'filename' => '',
19+
'filepath' => '',
20+
'params' => [],
21+
];

0 commit comments

Comments
 (0)