A Flask-based web application for regulatory compliance and risk monitoring in the telecommunications sector. The tool implements a Key Risk Indicator (KRI) scoring model derived from Pakistan's telecom cybersecurity framework, supporting multi-tenant organisation isolation, historical trend analysis, side-by-side comparison, report export (CSV/PDF), and full audit logging.
- Requirements
- Installation
- Configuration
- Database Setup
- Running the Application
- Default Credentials
- Features
- Risk Scoring Model
- Project Structure
- Troubleshooting
- Development Notes
- Python 3.8 or higher
- MySQL or MariaDB server
- pip3 package manager
- A Unix-like environment
-
Clone or extract the project archive to a working directory.
-
Create and activate a virtual environment:
python3 -m venv venv
source venv/bin/activate- Install dependencies:
pip install -r requirements.txt- Copy the example environment file:
cp .env.example .env- Edit
.envwith your database credentials. Use the dedicated application user created in the next section (notroot):
MYSQL_HOST=localhost
MYSQL_USER=pta_app
MYSQL_PASSWORD=your_app_user_password
MYSQL_DB=PTA_RiskWatch_DB
SECRET_KEY=your_secure_secret_keySecurity note: Never commit
.envto version control. It is listed in.gitignoreby default.
The application uses MySQL or MariaDB. On Kali Linux, MariaDB is the default provider.
sudo systemctl enable --now mysql
sudo systemctl start mysqlMariaDB on Kali uses Unix socket authentication for root by default. Open a root session:
sudo mysqlIf you have previously set a root password, use:
mysql -u root -pRather than running the application as root, create a least-privilege user that has access only to the application database:
CREATE DATABASE IF NOT EXISTS PTA_RiskWatch_DB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'pta_app'@'localhost' IDENTIFIED BY 'leopard324';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON PTA_RiskWatch_DB.* TO 'pta_app'@'localhost';
FLUSH PRIVILEGES;
EXIT;
CREATE,DROP,INDEX, andALTERare needed on first run so Flask-SQLAlchemy can create the schema. Once the tables exist you can tighten permissions further by revoking those four grants and keeping onlySELECT, INSERT, UPDATE, DELETE.
mysql -u pta_app -p PTA_RiskWatch_DBA successful login confirms the user and database are ready.
Ensure the virtual environment is activated and the database server is running, then:
python3 app.pyOn first execution the application will automatically:
- Create all database tables via SQLAlchemy.
- Seed initial data: three telecom organisations (Telenor Pakistan, Jazz 5G Pakistan, Zong 5G), four user accounts (one admin + one per organisation), 14 KRI definitions with regulatory references, weights, and scoring thresholds, and sample KRI scores for the years 2015 through 2025.
The development server starts at http://127.0.0.1:5000.
To skip seeding (e.g. after the first run), the
seed_data()function exits early if any organisation already exists in the database.
| Username | Role | Password | Organisation |
|---|---|---|---|
admin |
admin | admin123 |
None (global access) |
telenor |
org_user | telenor123 |
Telenor Pakistan |
jazz |
org_user | jazz123 |
Jazz 5G Pakistan |
zong |
org_user | zong123 |
Zong 5G (CMPak) |
All default passwords must be changed before any production or shared deployment.
Passwords can be updated from the My Profile page after logging in.
- Session-based login/logout using Flask-Login.
- Two roles:
admin(global access to all organisations) andorg_user(restricted to their own organisation). - Public company self-registration creates a new organisation and an
org_useraccount in one step. - Users can update their username and password from their profile page.
- Displays the overall weighted risk score (0–100) and risk level (Low / Medium / High / Critical) for a selected organisation and year.
- KRI summary cards show the count of indicators at each risk level.
- Full KRI table with raw value, numerical score, weight, and contribution - colour-coded by risk level.
- Year selector and (for admins) organisation selector for quick navigation.
- Direct links to edit scores or add data for a new year.
- One-click CSV and PDF report download.
- Admin and org_user roles can enter or update raw KRI values (0–100%) for any organisation and year.
- Numerical scores and weighted contributions are computed automatically on save using the configured threshold bands.
- Existing values are pre-filled to allow partial updates.
- Line chart (Chart.js) showing overall risk score over all available years for a selected organisation.
- Tabular history with progress bars and risk-level badges per year.
- Admin users can switch between organisations via a dropdown.
- Company comparison (admin only): select 2–3 organisations and a year to produce a bar chart and a KRI-level breakdown table side-by-side.
- Year comparison (all users): select 2–3 years for your own organisation to visualise how scores have shifted over time.
- CSV: KRI name, raw value, numerical score, weight, and contribution - plus an overall score row at the end.
- PDF: formatted report via ReportLab with automatic page breaks for long KRI lists.
- Access is restricted so org_users can only export their own organisation's data.
- Every login, logout, score update, report download, comparison, and registration event is recorded with a timestamp, the acting user, action type, and descriptive detail.
- Audit log viewer (admin only) shows the 200 most recent entries.
The overall risk score is a weighted sum across 14 KRIs:
Overall Score = Σ (numerical_score_i × weight_i / 100)
Each KRI maps a raw percentage value to a numerical score via threshold bands:
| Band | Numerical Score | Overall Contribution |
|---|---|---|
| Low | 10 | Low |
| Medium | 40 | Moderate |
| High | 70 | High |
| Critical | 100 | Maximum |
The final score maps to a risk level as follows:
| Score Range | Risk Level |
|---|---|
| 0 – 25 | Low |
| 26 – 50 | Medium |
| 51 – 75 | High |
| 76 – 100 | Critical |
KRI weights, threshold boundaries, regulatory references, and measurement methods are stored as data (not code) in the kri_definitions table, making reconfiguration possible without application changes.
The 14 KRIs cover obligations drawn from: PECA 2016, NCSP 2021, CTDISR-2025, PISF 2025, and CERT Rules 2023.
CSG-Project/
├── app.py # Application factory and entry point
├── extensions.py # Shared SQLAlchemy and LoginManager instances
├── models.py # ORM models: Organisation, User, KRIDefinition, KRIScore, AuditLog
├── routes.py # All Flask routes and business logic (Blueprint)
├── seed.py # Database seeder: organisations, users, KRI definitions, sample scores
├── requirements.txt # Python dependencies
├── .env # Runtime environment variables (not committed)
├── .env.example # Environment variable template
├── .gitignore
├── README.md
└── templates/
├── base.html # Shared layout: navbar, footer, Bootstrap 5, Chart.js
├── login.html # Standalone login page
├── register.html # Public company registration page
├── dashboard.html # Main KRI dashboard
├── add_scores.html # KRI score entry/edit form
├── trends.html # Historical trend chart and table
├── compare.html # Company and year comparison engine
├── profile.html # User profile and password change
└── audit.html # Audit log viewer (admin only)
The database server is not running. Start it with:
sudo systemctl start mysqlThe application user may not have been created, or the password in .env does not match. Re-run the CREATE USER and GRANT statements from the Database Setup section, then verify with:
mysql -u pta_app -p PTA_RiskWatch_DBThe database was not created. Run:
CREATE DATABASE IF NOT EXISTS PTA_RiskWatch_DB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;The virtual environment is not active, or dependencies were not installed:
source venv/bin/activate
pip install -r requirements.txtThe seeder exits early if any organisation row is present. To reseed from scratch, drop and recreate the database, then restart the application:
DROP DATABASE PTA_RiskWatch_DB;
CREATE DATABASE PTA_RiskWatch_DB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON PTA_RiskWatch_DB.* TO 'pta_app'@'localhost';To fix this issue, please run the following commands in the order.
sudo mkdir -p /run/mysqld
sudo chown mysql:mysql /run/mysqld
sudo chmod 755 /run/mysqld
sudo service mysql restart
- All database queries go through SQLAlchemy's ORM - no raw string interpolation, preventing SQL injection.
- Passwords are hashed with Werkzeug's
generate_password_hash(PBKDF2-HMAC-SHA256 by default). - KRI thresholds are stored as JSON in the
kri_definitionstable; adjusting compliance thresholds requires only a data change, not a code deployment. - The
UniqueConstrainton(organization_id, year, kri_id)inKRIScoreprevents duplicate entries and makes upserts safe. - Chart data for the trends page is served via a lightweight
/api/trends_dataJSON endpoint, keeping the Jinja template free of large inline data blobs. - Bootstrap 5 and Chart.js are loaded from CDN - no local static assets are required.
This tool was developed as the semester project for Cyber Security Governance (CY-303) at the National Cyber Security Academy (NCSA), Air University, Islamabad. The KRI framework is based on Pakistan's published telecom cybersecurity regulations: PECA 2016, NCSP 2021, CTDISR-2025, PISF 2025, and CERT Rules 2023.
- Yasir Mehmood @TheLeopard65
- Shaheer Baig @Shaheer-Baig
- Kiran Hashmi @kiranhashmi