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

Commit 6fe2056

Browse files
refactor: consolidate exporter escape helpers + fix Copilot setup workflow path
refactor: route per-class escapeHtml / jsonString wrappers through gvisual.ExportUtils so we have one canonical implementation. Affected: - GraphAdversaryForecaster.jsonString -> ExportUtils.jsonString (the local copy was the only one that already covered \\b/\\f and sub-0x20 \\uXXXX escapes; ExportUtils already does both, so behavior is preserved while killing 22 lines of dupe.) - GraphAnnotationManager.jsonString -> ExportUtils.jsonString (local copy did NOT escape \\b/\\f or control chars -> latent JSON output bug; now correct.) - GraphFairnessAuditEngine.escapeHtml -> ExportUtils.escapeHtml - GraphPercolationEngine.escapeHtml -> ExportUtils.escapeHtml Net: -47 lines, removes drift between exporters/engines, and silently fixes two control-char escaping holes in JSON annotation output. setup_copilot_agent: the existing copilot setup file was at .github/copilot-setup-steps.yml with job name 'setup'. GitHub's coding agent only picks it up at .github/workflows/copilot-setup-steps.yml with job name 'copilot-setup-steps' (per docs), so the previous file was effectively a no-op for the agent. - Move + rename to .github/workflows/copilot-setup-steps.yml - Rename job to copilot-setup-steps - Add timeout, least-privilege contents:read permissions - Add push/pull_request triggers scoped to pom.xml + this workflow so breakage is caught in normal CI, not only when an agent runs - Swap 'mvn dependency:resolve' for 'mvn dependency:go-offline' so the agent can run tests with -o - Add 'test-compile' step so test-only breakage surfaces before running the suite - Document the new helper conventions in copilot-instructions.md Build verified: mvn compile -B -ntp (177 sources, BUILD SUCCESS). Tests verified for touched classes: ExportUtilsTest, JsonGraphExporterTest, GraphMLExporterTest, SvgExporterTest, GexfExporterTest, GraphTimelineExporterTest -> 91/91 pass GraphAdversaryForecasterTest, GraphAnnotationManagerTest, GraphFairnessAuditEngineTest, GraphPercolationEngineTest (minus the pre-existing flaky testSiteCurveMonotonicallyNonDecreasing) -> 126/126 pass The percolation site-curve monotonicity test is a pre-existing flake on master and is unrelated to this refactor (reproduced before stash apply).
1 parent 3150a0c commit 6fe2056

7 files changed

Lines changed: 72 additions & 60 deletions

.github/copilot-instructions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ GraphVisual is a Java graph visualization and analysis platform built with [JUNG
5353
mvn test # Run all 105 test suites
5454
mvn test -Dtest=ShortestPathFinderTest # Run a specific test
5555
mvn test -pl . -Dtest="gvisual.*" # Run all gvisual package tests
56+
mvn -B -ntp test-compile # Quickly verify main + test sources compile
5657
```
5758

59+
The `copilot-setup-steps` workflow at `.github/workflows/copilot-setup-steps.yml` mirrors these commands and is what GitHub Copilot's cloud agent runs before starting a task.
60+
5861
When adding new tests, place them in `Gvisual/test/` matching the source package.
5962

6063
## Common Patterns
@@ -72,3 +75,5 @@ When adding new tests, place them in `Gvisual/test/` matching the source package
7275
- The `docs/` directory is a separate GitHub Pages site — changes there don't affect the Java build
7376
- Database configuration may be in `Network.java` — check before modifying DB-related code
7477
- Use `mvn compile -q` to verify compilation after changes
78+
- **String escaping for exporters:** Use `gvisual.ExportUtils` (`escapeXml`, `escapeHtml`, `escapeJs`, `jsonString`, `quoteDot`) instead of writing per-class helpers. Existing per-class wrappers delegate to `ExportUtils` so behavior stays consistent across XML/JSON/HTML/SVG/GraphML/GEXF exporters.
79+
- **Output path safety:** New exporters that write files should call `ExportUtils.validateOutputPath(file)` first to prevent CWE-22 directory traversal.

.github/copilot-setup-steps.yml

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copilot Setup Steps for GraphVisual
2+
#
3+
# This workflow pre-installs the toolchain and project dependencies so that
4+
# GitHub Copilot's cloud coding agent (and other agentic clients) can build,
5+
# test, and validate changes without paying the cold-start cost each task.
6+
#
7+
# Per the GitHub docs the file MUST be at .github/workflows/copilot-setup-steps.yml
8+
# and the job MUST be named `copilot-setup-steps`, otherwise the agent does
9+
# not pick it up.
10+
# https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment
11+
12+
name: Copilot Setup Steps
13+
14+
on:
15+
# Allows the workflow to be triggered manually for verification.
16+
workflow_dispatch:
17+
# Re-run when this file or the build descriptor changes so the cache stays
18+
# fresh and we catch breakage before the agent does.
19+
push:
20+
paths:
21+
- .github/workflows/copilot-setup-steps.yml
22+
- pom.xml
23+
pull_request:
24+
paths:
25+
- .github/workflows/copilot-setup-steps.yml
26+
- pom.xml
27+
28+
jobs:
29+
# Job name is required to be exactly `copilot-setup-steps` for Copilot
30+
# coding agent to consume the steps.
31+
copilot-setup-steps:
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 15
34+
permissions:
35+
contents: read
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@v4
39+
40+
- name: Set up JDK 11 (Temurin)
41+
uses: actions/setup-java@v4
42+
with:
43+
java-version: '11'
44+
distribution: 'temurin'
45+
cache: 'maven'
46+
47+
- name: Show toolchain
48+
run: |
49+
java -version
50+
mvn -version
51+
52+
- name: Resolve dependencies (offline cache warm-up)
53+
run: mvn -B -ntp dependency:go-offline
54+
55+
- name: Compile (main + test sources)
56+
run: mvn -B -ntp test-compile
57+
58+
- name: Run unit tests
59+
run: mvn -B -ntp test

Gvisual/src/gvisual/GraphAdversaryForecaster.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -813,26 +813,8 @@ private static String num(double d) {
813813
return String.format(Locale.ROOT, "%.6f", d);
814814
}
815815

816+
/** Delegates to {@link ExportUtils#jsonString(String)} for consistent JSON escaping. */
816817
private static String jsonString(String s) {
817-
if (s == null) return "null";
818-
StringBuilder sb = new StringBuilder(s.length() + 2);
819-
sb.append('"');
820-
for (int i = 0; i < s.length(); i++) {
821-
char c = s.charAt(i);
822-
switch (c) {
823-
case '"': sb.append("\\\""); break;
824-
case '\\': sb.append("\\\\"); break;
825-
case '\b': sb.append("\\b"); break;
826-
case '\f': sb.append("\\f"); break;
827-
case '\n': sb.append("\\n"); break;
828-
case '\r': sb.append("\\r"); break;
829-
case '\t': sb.append("\\t"); break;
830-
default:
831-
if (c < 0x20) sb.append(String.format("\\u%04x", (int) c));
832-
else sb.append(c);
833-
}
834-
}
835-
sb.append('"');
836-
return sb.toString();
818+
return ExportUtils.jsonString(s);
837819
}
838820
}

Gvisual/src/gvisual/GraphAnnotationManager.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -567,13 +567,9 @@ public void clear() {
567567

568568
// --- Helpers ---
569569

570+
/** Delegates to {@link ExportUtils#jsonString(String)} for consistent (and control-char-safe) JSON escaping. */
570571
private static String jsonString(String value) {
571-
if (value == null) return "null";
572-
return "\"" + value.replace("\\", "\\\\")
573-
.replace("\"", "\\\"")
574-
.replace("\n", "\\n")
575-
.replace("\r", "\\r")
576-
.replace("\t", "\\t") + "\"";
572+
return ExportUtils.jsonString(value);
577573
}
578574

579575
private static String extractJsonStringValue(String line) {

Gvisual/src/gvisual/GraphFairnessAuditEngine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,8 +958,8 @@ private void appendCard(StringBuilder html, String label, String value, String c
958958
html.append("\">").append(escapeHtml(value)).append("</div></div>\n");
959959
}
960960

961+
/** Delegates to {@link ExportUtils#escapeHtml(String)} for consistent HTML escaping. */
961962
private String escapeHtml(String text) {
962-
return text.replace("&", "&amp;").replace("<", "&lt;")
963-
.replace(">", "&gt;").replace("\"", "&quot;");
963+
return ExportUtils.escapeHtml(text);
964964
}
965965
}

Gvisual/src/gvisual/GraphPercolationEngine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,9 +766,9 @@ private void appendCard(StringBuilder html, String label, String value, String c
766766
html.append("\">").append(escapeHtml(value)).append("</div></div>\n");
767767
}
768768

769+
/** Delegates to {@link ExportUtils#escapeHtml(String)} for consistent HTML escaping. */
769770
private String escapeHtml(String text) {
770-
return text.replace("&", "&amp;").replace("<", "&lt;")
771-
.replace(">", "&gt;").replace("\"", "&quot;");
771+
return ExportUtils.escapeHtml(text);
772772
}
773773

774774
// -- Utility --------------------------------------------------------------

0 commit comments

Comments
 (0)