11package com .github .serj .jcapslock .agent ;
22
33import com .fasterxml .jackson .annotation .JsonProperty ;
4+ import com .fasterxml .jackson .databind .JsonNode ;
45import com .fasterxml .jackson .databind .ObjectMapper ;
56import com .fasterxml .jackson .dataformat .yaml .YAMLFactory ;
67import org .objectweb .asm .Type ;
78
89import java .io .File ;
910import java .lang .instrument .Instrumentation ;
11+ import java .util .Arrays ;
1012import java .util .HashMap ;
1113import java .util .HashSet ;
1214import java .util .List ;
@@ -73,6 +75,24 @@ public static Instrumentation getInstrumentation() {
7375 return instrumentation ;
7476 }
7577
78+ /**
79+ * All enforceable capabilities (excludes UNSPECIFIED, SAFE, UNANALYZED).
80+ */
81+ private static final Set <String > ALL_CAPABILITIES = new HashSet <>(Arrays .asList (
82+ "CAPABILITY_FILES" ,
83+ "CAPABILITY_NETWORK" ,
84+ "CAPABILITY_RUNTIME" ,
85+ "CAPABILITY_READ_SYSTEM_STATE" ,
86+ "CAPABILITY_MODIFY_SYSTEM_STATE" ,
87+ "CAPABILITY_OPERATING_SYSTEM" ,
88+ "CAPABILITY_SYSTEM_CALLS" ,
89+ "CAPABILITY_ARBITRARY_EXECUTION" ,
90+ "CAPABILITY_CGO" ,
91+ "CAPABILITY_UNSAFE_POINTER" ,
92+ "CAPABILITY_REFLECT" ,
93+ "CAPABILITY_EXEC"
94+ ));
95+
7696 private static void loadPolicy (String policyPath ) {
7797 File policyFile = new File (policyPath );
7898 if (!policyFile .exists ()) {
@@ -86,14 +106,42 @@ private static void loadPolicy(String policyPath) {
86106 ObjectMapper mapper = new ObjectMapper (new YAMLFactory ());
87107 PolicyFile policy = mapper .readValue (policyFile , PolicyFile .class );
88108
109+ // Check if any policy uses DEFAULT - if so, load snapshot
110+ Map <String , Set <String >> snapshotCapabilities = null ;
111+ boolean hasDefault = policy .policies != null && policy .policies .stream ()
112+ .anyMatch (e -> e .blocked != null && e .blocked .contains ("DEFAULT" ));
113+
114+ if (hasDefault ) {
115+ snapshotCapabilities = loadSnapshotCapabilities (policyFile .getParentFile ());
116+ }
117+
89118 Map <String , Set <String >> capabilityToPackages = new HashMap <>();
90119 if (policy .policies != null ) {
91120 for (PolicyEntry entry : policy .policies ) {
92121 if (entry .blocked != null ) {
93- for (String capability : entry .blocked ) {
94- capabilityToPackages
95- .computeIfAbsent (capability , k -> new HashSet <>())
96- .add (entry .pkg );
122+ List <String > effectiveBlocked = entry .blocked ;
123+
124+ // Expand DEFAULT to all capabilities not in snapshot for this package
125+ if (entry .blocked .contains ("DEFAULT" )) {
126+ Set <String > allowedFromSnapshot = snapshotCapabilities != null
127+ ? snapshotCapabilities .getOrDefault (entry .pkg , Set .of ())
128+ : Set .of ();
129+
130+ Set <String > toBlock = new HashSet <>(ALL_CAPABILITIES );
131+ toBlock .removeAll (allowedFromSnapshot );
132+ effectiveBlocked = List .copyOf (toBlock );
133+
134+ Log .info ("DEFAULT expansion for " + entry .pkg +
135+ ": allowed from snapshot=" + allowedFromSnapshot +
136+ ", blocking=" + toBlock );
137+ }
138+
139+ for (String capability : effectiveBlocked ) {
140+ if (!"DEFAULT" .equals (capability )) {
141+ capabilityToPackages
142+ .computeIfAbsent (capability , k -> new HashSet <>())
143+ .add (entry .pkg );
144+ }
97145 }
98146 }
99147 }
@@ -110,6 +158,42 @@ private static void loadPolicy(String policyPath) {
110158 }
111159 }
112160
161+ /**
162+ * Loads capabilities from snapshot.json in the given directory.
163+ * Returns a map from package name to set of capabilities.
164+ */
165+ private static Map <String , Set <String >> loadSnapshotCapabilities (File directory ) {
166+ File snapshotFile = new File (directory , "snapshot.json" );
167+ if (!snapshotFile .exists ()) {
168+ Log .warn ("Snapshot file not found: " + snapshotFile + " (DEFAULT blocking may not work correctly)" );
169+ return Map .of ();
170+ }
171+
172+ try {
173+ ObjectMapper mapper = new ObjectMapper ();
174+ JsonNode root = mapper .readTree (snapshotFile );
175+ JsonNode capabilityInfoList = root .get ("capability_info" );
176+
177+ Map <String , Set <String >> result = new HashMap <>();
178+ if (capabilityInfoList != null && capabilityInfoList .isArray ()) {
179+ for (JsonNode info : capabilityInfoList ) {
180+ String packageName = info .has ("package_name" ) ? info .get ("package_name" ).asText () : null ;
181+ String capability = info .has ("capability" ) ? info .get ("capability" ).asText () : null ;
182+
183+ if (packageName != null && capability != null ) {
184+ result .computeIfAbsent (packageName , k -> new HashSet <>()).add (capability );
185+ }
186+ }
187+ }
188+
189+ Log .info ("Loaded snapshot with " + result .size () + " packages" );
190+ return result ;
191+ } catch (Exception e ) {
192+ Log .error ("Failed to load snapshot: " + e .getMessage ());
193+ return Map .of ();
194+ }
195+ }
196+
113197 public static class PolicyFile {
114198 public List <PolicyEntry > policies ;
115199 }
0 commit comments