I have a usecase where I want to allow only classes from certain packages to read from a directory of files. This is achievable through grant policy in SecurityManager using a file like
grant codeBase "file:/path/to/application.jar" {
permission java.io.FilePermission "/path/to/directory/*", "read,write";
permission java.lang.RuntimePermission "accessClassInPackage.allowed.package";
};
but given that SecurityManager is deprecated, I was thinking if there is a way to achieve the same using byte-buddy.
Edit 1
I wrote this FileInterceptor to intercept all file reads. I am trying to do it at a BufferedReader level
public class FileInterceptor {
public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {
System.out.println("Inside premain");
File temp = Files.createTempDirectory("tmp").toFile();
Map<TypeDescription.ForLoadedType, byte[]> injectTypes = new HashMap<>();
injectTypes.put(new TypeDescription.ForLoadedType(BufferedReaderAdvice.class),
ClassFileLocator.ForClassLoader.read(BufferedReaderAdvice.class));
ClassInjector.UsingInstrumentation.of(temp,
ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation).inject(injectTypes);
new AgentBuilder.Default()
.ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
.with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(instrumentation, temp))
.type(ElementMatchers.named("java.io.BufferedReader"))
.transform((builder, typeDescription, classLoader, module, protectionDomain) ->
builder.visit(Advice.to(BufferedReaderAdvice.class).on(
ElementMatchers.isConstructor().and(ElementMatchers.any()
)))) // Intercept calls to BufferedReader(InputStreamReader)
.installOn(instrumentation);
}
public static class BufferedReaderAdvice {
@Advice.OnMethodEnter()
public static void enter(@Advice.This BufferedReader bufferedReader, @Advice.Argument(0) Reader fileReader) {
String path = new File(((InputStreamReader) fileReader).getEncoding()).getAbsolutePath(); // Get the absolute path of the file
System.out.println("Inside enter");
if (path.startsWith("/path/") &&
!bufferedReader.getClass().getPackage().getName().startsWith("com.whitelist")) {
// Deny access for files in the path of interest but not being accessed by a package of interest
throw new SecurityException("Access denied");
}
}
}
}
I am trying to intercept calls via Files.readAllLines(path) in another project which uses the above as a javaagent. What I see happening is that the Inside premain gets invoked but the enter method doesn't get invoked. What am I missing in this case?
Edit 2
public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {
System.out.println("Inside premain");
new AgentBuilder.Default().disableClassFormatChanges().with(RETRANSFORMATION).ignore(none())
.type(ElementMatchers.named("java.io.BufferedReader"))
.transform((builder, typeDescription, classLoader, module, protectionDomain) ->
builder.visit(Advice.to(BufferedReaderAdvice.class).on(isConstructor())))
.installOn(instrumentation);
public static class BufferedReaderAdvice {
@Advice.OnMethodEnter()
public static void enter(@Advice.Origin Constructor constructor, @Advice.AllArguments Object[] params) {
System.out.println("Inside enter");
System.out.println("enter-------" + constructor.getDeclaringClass());
String callingPackage = constructor.getDeclaringClass().getPackage().getName();
InputStreamReader streamReader = (InputStreamReader) params[0];
String path = new File(streamReader.getEncoding()).getAbsolutePath(); // Get the absolute path of the file
System.out.println("callingPackage-->" + callingPackage);
System.out.println("path-->" + path);
for (Object param:params) {
System.out.println(param);
}
}
}
Calling code:
public void readFile(String filePath) {
System.out.println("Class:"+getClass().getProtectionDomain().getCodeSource().getLocation());
Path path = Paths.get(filePath);
try {
List<String> lines = Files.readAllLines(path);
System.out.println(lines);
} catch (IOException ex) {
// handle exception...
}
}