Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 0f61ed3

Browse files
fix: input validation for edge parser and path traversal prevention, add SECURITY.md
security_fix: Two hardening improvements: 1. Edge-list parser (Main.java): Previously crashed with ArrayIndexOutOfBoundsException on malformed lines (fewer than 4 fields) and NumberFormatException on non-numeric weights. Now validates field count (>= 4), catches NumberFormatException, and rejects NaN/Infinity weights. Malformed lines are skipped with stderr warnings instead of crashing the application. 2. Network.generateFile() path traversal (Network.java): Added canonical path validation ensuring the output file resolves within the working directory. Prevents directory traversal via paths like '../../etc/something'. Throws SecurityException on violation. doc_update: Added comprehensive SECURITY.md documenting: - Security model and threat categories with mitigation status - Database security (parameterized queries, credential management) - File I/O security (path validation, input validation, XML escaping) - Analysis engine defensive patterns (null checks, immutable results) - Dependency audit with upgrade recommendations - Vulnerability reporting guidance
1 parent 7378bbe commit 0f61ed3

3 files changed

Lines changed: 167 additions & 2 deletions

File tree

Gvisual/src/app/Network.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ public class Network {
1919
* Connects to database and writes out the edge-list from the meeting DB table, forming edges of kind:
2020
* friends, classmates, study-groups, strangers and familiar strangers (depending upon parameters).
2121
*
22-
* @param path
22+
* <p>The output path is validated to prevent directory traversal —
23+
* it must resolve to a location within the current working directory.</p>
24+
*
25+
* @param path output file path (must be within the working directory)
2326
* @param Month
2427
* @param Date
2528
* @param dThresF
@@ -36,6 +39,15 @@ public class Network {
3639
*/
3740
public static void generateFile(String path, String Month, String Date, int dThresF, int CThresF, int dThresFS, int CThresFS, int dThresC, int CThresC, int dThresS, int CThresS, int dThresSg, int CThresSg) throws Exception {
3841

42+
// Validate output path — prevent directory traversal attacks
43+
File outputFile = new File(path).getCanonicalFile();
44+
File workingDir = new File(".").getCanonicalFile();
45+
if (!outputFile.toPath().startsWith(workingDir.toPath())) {
46+
throw new SecurityException(
47+
"Output path must be within the working directory. "
48+
+ "Resolved path: " + outputFile.getAbsolutePath());
49+
}
50+
3951
System.out.println("connecting...");
4052

4153
// Parameterized query template for location-based meeting queries.

Gvisual/src/gvisual/Main.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,15 +643,35 @@ public void addGraph() throws ParserConfigurationException, IOException, SAXExce
643643
} else {
644644
if (count == 0) {
645645
final String[] nodeParam = line.split(" ");
646+
if (nodeParam.length < 1 || nodeParam[0].isEmpty()) {
647+
continue; // skip malformed node lines
648+
}
646649
g.addVertex(nodeParam[0]);
647650
//graphLayout.setLocation(nodeParam[0], new Point(Integer.parseInt(nodeParam[1]), Integer.parseInt(nodeParam[2])));
648651
} else {
649652

650653

651654

652655
String[] edgeParam = line.split(" ");
656+
// Validate edge line: need at least 4 fields
657+
// (type, vertex1, vertex2, weight)
658+
if (edgeParam.length < 4) {
659+
System.err.println("Skipping malformed edge line: " + line);
660+
continue;
661+
}
662+
float weight;
663+
try {
664+
weight = Float.parseFloat(edgeParam[3]);
665+
} catch (NumberFormatException nfe) {
666+
System.err.println("Skipping edge with invalid weight: " + line);
667+
continue;
668+
}
669+
if (Float.isNaN(weight) || Float.isInfinite(weight)) {
670+
System.err.println("Skipping edge with non-finite weight: " + line);
671+
continue;
672+
}
653673
edge curEdge = new edge(edgeParam[0], edgeParam[1], edgeParam[2]);
654-
curEdge.setWeight(Float.parseFloat(edgeParam[3]));
674+
curEdge.setWeight(weight);
655675

656676
// Classify edge by type and add to the appropriate list
657677
EdgeType edgeType = EdgeType.fromCode(edgeParam[0]);

SECURITY.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Security
2+
3+
This document describes the security model, threat surface, and
4+
mitigations in GraphVisual.
5+
6+
## Reporting Vulnerabilities
7+
8+
If you discover a security vulnerability, please **do not** open a public
9+
issue. Instead, email the maintainer directly or use GitHub's private
10+
vulnerability reporting feature.
11+
12+
## Security Model
13+
14+
GraphVisual is a desktop Swing application that processes Bluetooth
15+
proximity data from a PostgreSQL database and renders social network
16+
graphs. Its security posture is shaped by two distinct surfaces:
17+
18+
1. **Database access** — connects to PostgreSQL using credentials from
19+
environment variables
20+
2. **File I/O** — reads edge-list files and writes exports (GraphML, PNG)
21+
22+
### Threat Categories
23+
24+
| Threat | Surface | Status |
25+
|--------|---------|--------|
26+
| SQL injection | Database queries | ✅ Mitigated — all queries use `PreparedStatement` with parameterized bindings |
27+
| Credential exposure | Database connection | ✅ Mitigated — credentials read from `DB_HOST`, `DB_USER`, `DB_PASS` environment variables; never hardcoded |
28+
| XML injection in exports | GraphML export | ✅ Mitigated — `GraphMLExporter.escapeXml()` escapes `&`, `<`, `>`, `"`, `'` |
29+
| Path traversal | File output | ✅ Mitigated — `Network.generateFile()` validates output path is within working directory |
30+
| Malformed input files | Edge-list parser | ✅ Mitigated — validates field count and weight format before constructing edge objects |
31+
| NaN/Infinity weights | Edge-list parser | ✅ Mitigated — rejects `NaN` and `Infinity` weight values |
32+
| Denial of service (large graphs) | Graph analysis | ⚠️ Partial — analyzers have no built-in size limits; very large graphs can exhaust memory |
33+
34+
## Database Security
35+
36+
### Parameterized Queries
37+
38+
All SQL queries in the `app/` package use `PreparedStatement` with `?`
39+
placeholders. There is no string concatenation of user input into SQL:
40+
41+
- `Network.java` — 5 parameterized queries for relationship extraction
42+
- `matchImei.java` — 4 parameterized queries for IMEI matching
43+
- `addLocation.java` — 3 parameterized queries for WiFi AP lookup
44+
- `findMeetings.java` — parameterized queries for meeting extraction
45+
46+
### Credential Management
47+
48+
Database credentials are loaded exclusively from environment variables
49+
via `Util.java`:
50+
51+
```java
52+
String host = envOrDefault("DB_HOST", DEFAULT_HOST);
53+
String user = requireEnv("DB_USER"); // throws if missing
54+
String pass = requireEnv("DB_PASS"); // throws if missing
55+
```
56+
57+
Missing required variables cause an immediate `IllegalStateException`
58+
with a clear error message rather than falling through to a default
59+
or null credential.
60+
61+
## File I/O Security
62+
63+
### Output Path Validation
64+
65+
`Network.generateFile()` validates that the output file path resolves
66+
to a location within the current working directory using canonical path
67+
comparison:
68+
69+
```java
70+
File outputFile = new File(path).getCanonicalFile();
71+
File workingDir = new File(".").getCanonicalFile();
72+
if (!outputFile.toPath().startsWith(workingDir.toPath())) {
73+
throw new SecurityException("Output path must be within the working directory.");
74+
}
75+
```
76+
77+
This prevents directory traversal attacks via paths like
78+
`../../etc/crontab`.
79+
80+
### Edge-List Input Validation
81+
82+
The edge-list parser in `Main.java` validates each line before
83+
constructing edge objects:
84+
85+
- **Field count** — requires at least 4 fields (type, vertex1, vertex2,
86+
weight); malformed lines are skipped with a warning
87+
- **Weight parsing** — catches `NumberFormatException` from invalid
88+
weight strings
89+
- **Non-finite weights** — rejects `NaN` and `Infinity` values that
90+
could corrupt analysis results
91+
92+
### GraphML Export
93+
94+
`GraphMLExporter.escapeXml()` handles all five XML special characters
95+
(`& < > " '`), preventing injection of arbitrary XML elements or
96+
attributes into exported files.
97+
98+
## Analysis Engine Security
99+
100+
All analyzer constructors validate their input:
101+
102+
```java
103+
if (graph == null) {
104+
throw new IllegalArgumentException("Graph must not be null");
105+
}
106+
```
107+
108+
Result objects wrap collections in `Collections.unmodifiable*()` to
109+
prevent mutation after creation.
110+
111+
## Dependencies
112+
113+
| Dependency | Version | Notes |
114+
|------------|---------|-------|
115+
| JUNG | 2.0.1 | Graph library — no known CVEs |
116+
| PostgreSQL JDBC | 8.3-604 | Legacy driver — consider upgrading for TLS improvements |
117+
| Commons IO | 1.4 | File utilities — consider upgrading for security patches |
118+
| JUnit | 4.13.2 | Test-only dependency |
119+
120+
### Recommendations
121+
122+
- **Upgrade PostgreSQL JDBC** to 42.x for TLS 1.3 support and
123+
security fixes
124+
- **Upgrade Commons IO** to 2.x for path traversal fixes in utility
125+
methods
126+
- **Run with least-privilege database credentials** — the application
127+
only needs SELECT on `nic_aziala` tables and SELECT/INSERT/UPDATE on
128+
`nic_apps` tables
129+
130+
## CodeQL
131+
132+
This repository has [CodeQL](https://github.com/sauravbhattacharya001/GraphVisual/actions)
133+
configured for automated security scanning on every push.

0 commit comments

Comments
 (0)