Skip to content

Commit 0e00aeb

Browse files
committed
Add Log utility adapted from Jazzer
1 parent 9c72574 commit 0e00aeb

4 files changed

Lines changed: 124 additions & 13 deletions

File tree

agent/src/main/java/com/github/serj/jcapslock/agent/CapabilityTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public byte[] transform(ClassLoader loader, String className, Class<?> classBein
6161
cr.accept(cv, ClassReader.EXPAND_FRAMES);
6262
return cw.toByteArray();
6363
} catch (Exception e) {
64-
System.err.println(PolicyChecker.LOG_PREFIX + "Failed to transform " + className + ": " + e.getMessage());
64+
Log.error("Failed to transform " + className + ": " + e.getMessage());
6565
return null;
6666
}
6767
}

agent/src/main/java/com/github/serj/jcapslock/agent/CapslockAgent.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
public class CapslockAgent {
2323

2424
public static void premain(String args, Instrumentation inst) {
25-
System.err.println(PolicyChecker.LOG_PREFIX + "Runtime capability monitoring enabled");
25+
Log.info("Runtime capability monitoring enabled");
2626

2727
// Load policy if provided
2828
if (args != null && !args.isEmpty()) {
@@ -35,7 +35,7 @@ public static void premain(String args, Instrumentation inst) {
3535
.getCodeSource().getLocation().toURI().getPath();
3636
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(agentJarPath)));
3737
} catch (Exception e) {
38-
System.err.println(PolicyChecker.LOG_PREFIX + "Warning: Could not add to bootstrap classpath: " + e.getMessage());
38+
Log.warn("Could not add to bootstrap classpath: " + e.getMessage());
3939
}
4040

4141
inst.addTransformer(new CapabilityTransformer(), true);
@@ -51,7 +51,7 @@ public static void premain(String args, Instrumentation inst) {
5151
} catch (ClassNotFoundException e) {
5252
// Class not yet loaded, transformer will catch it later
5353
} catch (Exception e) {
54-
System.err.println(PolicyChecker.LOG_PREFIX + "Warning: Could not retransform " + javaClassName + ": " + e.getMessage());
54+
Log.warn("Could not retransform " + javaClassName + ": " + e.getMessage());
5555
}
5656
}
5757
}
@@ -63,11 +63,11 @@ public static void premain(String args, Instrumentation inst) {
6363
private static void loadPolicy(String policyPath) {
6464
File policyFile = new File(policyPath);
6565
if (!policyFile.exists()) {
66-
System.err.println(PolicyChecker.LOG_PREFIX + "Policy file not found: " + policyPath);
66+
Log.error("Policy file not found: " + policyPath);
6767
return;
6868
}
6969

70-
System.err.println(PolicyChecker.LOG_PREFIX + "Loading policy from: " + policyPath);
70+
Log.info("Loading policy from: " + policyPath);
7171

7272
// Simple YAML parsing (no external deps)
7373
Map<String, Set<String>> capabilityToPackages = new HashMap<>();
@@ -97,7 +97,7 @@ private static void loadPolicy(String policyPath) {
9797
}
9898
}
9999
} catch (Exception e) {
100-
System.err.println(PolicyChecker.LOG_PREFIX + "Failed to load policy: " + e.getMessage());
100+
Log.error("Failed to load policy: " + e.getMessage());
101101
return;
102102
}
103103

@@ -106,7 +106,7 @@ private static void loadPolicy(String policyPath) {
106106
String propName = "capslock.block." + entry.getKey();
107107
String propValue = String.join(",", entry.getValue());
108108
System.setProperty(propName, propValue);
109-
System.err.println(PolicyChecker.LOG_PREFIX + "Policy: " + entry.getKey() + " blocked for: " + propValue);
109+
Log.info("Policy: " + entry.getKey() + " blocked for: " + propValue);
110110
}
111111
}
112112
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2024 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* This file was adapted from Jazzer (https://github.com/CodeIntelligenceTesting/jazzer)
17+
* to keep the agent lean by avoiding external logging dependencies.
18+
*/
19+
20+
package com.github.serj.jcapslock.agent;
21+
22+
import java.io.PrintStream;
23+
24+
/**
25+
* Provides static functions that should be used for any kind of output emitted by the agent.
26+
*
27+
* <p>Output is printed to {@link System#err} and {@link System#out} until {@link
28+
* Log#fixOutErr(PrintStream, PrintStream)} is called, which locks in the {@link PrintStream}s to be
29+
* used from there on.
30+
*/
31+
public class Log {
32+
33+
private static final String PREFIX = "[CAPSLOCK] ";
34+
35+
// Don't use directly, always use getOut() and getErr() instead - when these fields haven't been
36+
// set yet, we want to resolve them dynamically as System.out and System.err, which may change
37+
// over the course of the agent's lifetime.
38+
private static PrintStream fixedOut;
39+
private static PrintStream fixedErr;
40+
41+
// Whether to print debug messages. This is controlled by the CAPSLOCK_DEBUG environment variable.
42+
private static final boolean isDebug = System.getenv("CAPSLOCK_DEBUG") != null;
43+
44+
/** The {@link PrintStream}s to use for all output from this call on. */
45+
public static void fixOutErr(PrintStream out, PrintStream err) {
46+
if (out == null) {
47+
throw new IllegalArgumentException("out must not be null");
48+
}
49+
if (err == null) {
50+
throw new IllegalArgumentException("err must not be null");
51+
}
52+
Log.fixedOut = out;
53+
Log.fixedErr = err;
54+
}
55+
56+
public static void println(String message) {
57+
getErr().println(message);
58+
}
59+
60+
public static void structuredOutput(String output) {
61+
getOut().println(output);
62+
}
63+
64+
public static void debug(String message) {
65+
if (isDebug) {
66+
println(PREFIX, "DEBUG: " + message, null);
67+
}
68+
}
69+
70+
public static void info(String message) {
71+
println(PREFIX, message, null);
72+
}
73+
74+
public static void warn(String message) {
75+
warn(message, null);
76+
}
77+
78+
public static void warn(String message, Throwable t) {
79+
println(PREFIX, "WARN: " + message, t);
80+
}
81+
82+
public static void error(String message) {
83+
error(message, null);
84+
}
85+
86+
public static void error(Throwable t) {
87+
error(null, t);
88+
}
89+
90+
public static void error(String message, Throwable t) {
91+
println(PREFIX, "ERROR: " + message, t);
92+
}
93+
94+
private static void println(String prefix, String message, Throwable t) {
95+
PrintStream err = getErr();
96+
err.print(prefix);
97+
if (message != null) {
98+
err.println(message + (t != null ? ":" : ""));
99+
}
100+
if (t != null) {
101+
t.printStackTrace(err);
102+
}
103+
}
104+
105+
private static PrintStream getOut() {
106+
return fixedOut != null ? fixedOut : System.out;
107+
}
108+
109+
private static PrintStream getErr() {
110+
return fixedErr != null ? fixedErr : System.err;
111+
}
112+
}

agent/src/main/java/com/github/serj/jcapslock/agent/PolicyChecker.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
public class PolicyChecker {
1313

1414
public static final String PROP_PREFIX = "capslock.block.";
15-
public static final String LOG_PREFIX = "[CAPSLOCK] ";
1615

1716
/**
1817
* Check if capability is allowed for the current call stack.
@@ -32,7 +31,7 @@ public static void check(String capability) {
3231
if (blockedPackages != null) {
3332
String violator = findViolator(appFrames, blockedPackages);
3433
if (violator != null) {
35-
throw new SecurityException(LOG_PREFIX + capability + " blocked for " + violator);
34+
throw new SecurityException("[CAPSLOCK] " + capability + " blocked for " + violator);
3635
}
3736
}
3837
}
@@ -50,11 +49,11 @@ private static List<StackTraceElement> collectAppFrames() {
5049

5150
private static void logFrames(String capability, List<StackTraceElement> frames) {
5251
StringBuilder sb = new StringBuilder();
53-
sb.append(LOG_PREFIX).append(capability).append(":\n");
52+
sb.append(capability).append(":");
5453
for (StackTraceElement frame : frames) {
55-
sb.append(" ").append(frame.getClassName()).append(".").append(frame.getMethodName()).append("()\n");
54+
sb.append("\n ").append(frame.getClassName()).append(".").append(frame.getMethodName()).append("()");
5655
}
57-
System.err.print(sb);
56+
Log.info(sb.toString());
5857
}
5958

6059
private static String findViolator(List<StackTraceElement> frames, String[] blockedPackages) {

0 commit comments

Comments
 (0)