Skip to content

Commit 391cddc

Browse files
authored
PSEC-1879: Bugfix - II anchor deletion (#16)
* pin rust toolchain to 1.72.0 * create HierarchicPreferences from scratch in storeInternetIdentities * ignore IllegalArgumentException in ReqRespViewer * implement anchor deletion * refacotring, tests
1 parent da0212b commit 391cddc

File tree

7 files changed

+369
-53
lines changed

7 files changed

+369
-53
lines changed

java-extension/src/main/java/org/dfinity/ic/burp/IcHttpRequestResponseViewer.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,14 @@ public boolean isEnabledFor(HttpRequestResponse requestResponse) {
125125
return false;
126126
}
127127

128-
// TODO This throws an exception from time to time. See notes for stacktrace.
129-
// TODO java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
130-
HttpHeader icDecodedHeader = requestResponse.request().header(IcBurpExtension.IC_DECODED_HEADER_NAME);
128+
final HttpHeader icDecodedHeader;
129+
try {
130+
icDecodedHeader = requestResponse.request().header(IcBurpExtension.IC_DECODED_HEADER_NAME);
131+
} catch (IllegalArgumentException e) {
132+
// This throws an exception from time to time. See notes for stacktrace.
133+
// java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
134+
return false;
135+
}
131136
if (icDecodedHeader != null && icDecodedHeader.value().equals("True")) {
132137
return false;
133138
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.dfinity.ic.burp.model;
2+
3+
public enum PreferenceType {
4+
BOOLEAN,
5+
BYTE,
6+
INTEGER,
7+
LONG,
8+
SHORT,
9+
STRING
10+
}

java-extension/src/main/java/org/dfinity/ic/burp/storage/DataPersister.java

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
import org.dfinity.ic.burp.model.InternetIdentities;
1313
import org.dfinity.ic.burp.model.InternetIdentity;
1414
import org.dfinity.ic.burp.model.JWKIdentity;
15+
import org.dfinity.ic.burp.model.PreferenceType;
1516
import org.dfinity.ic.burp.tools.IcTools;
1617
import org.dfinity.ic.burp.tools.model.IcToolsException;
1718
import org.dfinity.ic.burp.tools.model.InterfaceType;
1819

1920
import java.util.Date;
2021
import java.util.Map;
2122
import java.util.Optional;
23+
import java.util.Set;
24+
25+
import static org.dfinity.ic.burp.storage.HierarchicPreferences.KEY_SEPARATOR;
2226

2327
public class DataPersister {
2428
public static final String IDENTITIES_KEY = "Identities";
@@ -170,17 +174,6 @@ private void generatePOTree() {
170174
this.rootPO.setChildObject(IC_KEY, icPO);
171175
}
172176

173-
private HierarchicPreferences generatePersistenceTree() {
174-
HierarchicPreferences icPref = HierarchicPreferences.from(preferences, IC_KEY).orElseGet(HierarchicPreferences::new);
175-
HierarchicPreferences iiPref = icPref.getChildObject(IDENTITIES_KEY);
176-
if (iiPref == null) {
177-
iiPref = new HierarchicPreferences();
178-
icPref.setChildObject(IDENTITIES_KEY, iiPref);
179-
}
180-
icPref.store(preferences, IC_KEY);
181-
return icPref;
182-
}
183-
184177
public JWKIdentity getDefaultIdentity() {
185178
// TODO The default JWK Identity is not yet persisted. A new one is generated when loading the extension.
186179
// PersistedObject icObject = rootPO.getChildObject("IC");
@@ -219,32 +212,46 @@ Burp Preferences (Preferences)
219212
Name of the IiState enum Pem file of the passkey Date II was created Date the passkey was activated
220213
and not present if not yet activated.
221214
*/
222-
log.logToOutput("Storing identities to Burp preference file.");
223-
HierarchicPreferences icPref = generatePersistenceTree();
224-
HierarchicPreferences identitiesPref = icPref.getChildObject(IDENTITIES_KEY);
225-
226-
for (Map.Entry<String, InternetIdentity> iiEntry : identities.getIdentities().entrySet()) {
227-
log.logToOutput("Storing identities with anchor: " + iiEntry.getKey());
228-
HierarchicPreferences iiPref = new HierarchicPreferences();
229-
InternetIdentity ii = iiEntry.getValue();
230-
231-
iiPref.setString(STATE_KEY, ii.getState().name());
232-
// The pem file of a passkey should never be empty.
233-
iiPref.setString(PASSKEY_KEY, ii.getPasskey().getPem().orElseThrow());
234-
iiPref.setLong(CREATION_DATE_KEY, ii.creationDate().getTime());
235-
Optional<Date> activationDate = ii.activationDate();
236-
activationDate.ifPresent(date -> iiPref.setLong(ACTIVATION_DATE_KEY, date.getTime()));
237-
log.logToOutput("Adding identity with: ("
238-
+ iiEntry.getKey() + ", "
239-
+ ii.getPasskey().getPem().orElseThrow() + ", "
240-
+ ii.getState().name() + ", "
241-
+ ii.creationDate() + ", "
242-
+ activationDate + ")");
243-
244-
log.logToOutput("Storing identities with HierarchicPreferences: " + iiPref);
245-
identitiesPref.setChildObject(iiEntry.getKey(), iiPref);
215+
Map<PreferenceType, Set<String>> storedKeys;
216+
if (identities.getIdentities().isEmpty()) {
217+
storedKeys = Map.of();
218+
} else {
219+
log.logToOutput("Storing identities to Burp preference file.");
220+
HierarchicPreferences icPref = new HierarchicPreferences();
221+
HierarchicPreferences identitiesPref = new HierarchicPreferences();
222+
icPref.setChildObject(IDENTITIES_KEY, identitiesPref);
223+
224+
for (Map.Entry<String, InternetIdentity> iiEntry : identities.getIdentities().entrySet()) {
225+
log.logToOutput("Storing identities with anchor: " + iiEntry.getKey());
226+
HierarchicPreferences iiPref = new HierarchicPreferences();
227+
InternetIdentity ii = iiEntry.getValue();
228+
229+
iiPref.setString(STATE_KEY, ii.getState().name());
230+
// The pem file of a passkey should never be empty.
231+
iiPref.setString(PASSKEY_KEY, ii.getPasskey().getPem().orElseThrow());
232+
iiPref.setLong(CREATION_DATE_KEY, ii.creationDate().getTime());
233+
Optional<Date> activationDate = ii.activationDate();
234+
activationDate.ifPresent(date -> iiPref.setLong(ACTIVATION_DATE_KEY, date.getTime()));
235+
log.logToOutput("Adding identity with: ("
236+
+ iiEntry.getKey() + ", "
237+
+ ii.getPasskey().getPem().orElseThrow() + ", "
238+
+ ii.getState().name() + ", "
239+
+ ii.creationDate() + ", "
240+
+ activationDate + ")");
241+
242+
log.logToOutput("Storing identities with HierarchicPreferences: " + iiPref);
243+
identitiesPref.setChildObject(iiEntry.getKey(), iiPref);
244+
}
245+
storedKeys = icPref.store(preferences, IC_KEY);
246246
}
247-
icPref.store(preferences, IC_KEY);
247+
248+
new PersisterUtils(preferences).deleteMatchingPreferences((type, key) -> {
249+
if (!key.startsWith(IC_KEY + KEY_SEPARATOR)) {
250+
// we do not delete preferences that do not fall into our namespace
251+
return false;
252+
}
253+
return !storedKeys.getOrDefault(type, Set.of()).contains(key);
254+
});
248255
}
249256

250257
public InternetIdentities getInternetIdentities() {

java-extension/src/main/java/org/dfinity/ic/burp/storage/HierarchicPreferences.java

Lines changed: 164 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.dfinity.ic.burp.storage;
22

33
import burp.api.montoya.persistence.Preferences;
4+
import org.dfinity.ic.burp.model.PreferenceType;
45

56
import java.util.HashMap;
7+
import java.util.HashSet;
68
import java.util.Map;
79
import java.util.Objects;
810
import java.util.Optional;
@@ -12,16 +14,16 @@
1214
import java.util.stream.Collectors;
1315

1416
public class HierarchicPreferences implements Preferences {
15-
private static final String KEY_SEPARATOR = "#";
16-
private static final String TYPE_VALUE_SEPARATOR = "$";
17-
private static final String RESERVED_CHARS = KEY_SEPARATOR + TYPE_VALUE_SEPARATOR;
18-
private static final String BOOLEAN_TYPE = "Boolean";
19-
private static final String BYTE_TYPE = "Byte";
20-
private static final String INTEGER_TYPE = "Integer";
21-
private static final String LONG_TYPE = "Long";
22-
private static final String SHORT_TYPE = "Short";
23-
private static final String STRING_TYPE = "String";
24-
private static final String CHILD_TYPE = "Child";
17+
public static final String KEY_SEPARATOR = "#";
18+
public static final String TYPE_VALUE_SEPARATOR = "$";
19+
public static final String RESERVED_CHARS = KEY_SEPARATOR + TYPE_VALUE_SEPARATOR;
20+
public static final String BOOLEAN_TYPE = "Boolean";
21+
public static final String BYTE_TYPE = "Byte";
22+
public static final String INTEGER_TYPE = "Integer";
23+
public static final String LONG_TYPE = "Long";
24+
public static final String SHORT_TYPE = "Short";
25+
public static final String STRING_TYPE = "String";
26+
public static final String CHILD_TYPE = "Child";
2527
private final Map<String, Integer> integers = new HashMap<>();
2628
private final Map<String, Boolean> booleans = new HashMap<>();
2729
private final Map<String, Byte> bytes = new HashMap<>();
@@ -235,12 +237,14 @@ private void storeInternal(Preferences preferences, String prefix) {
235237
}
236238
}
237239

238-
public void store(Preferences preferences, String key) {
240+
public Map<PreferenceType, Set<String>> store(Preferences preferences, String key) {
239241
assertValid(key);
240242
if (isEmpty())
241243
throw new RuntimeException("trying to store empty object");
242244

243-
storeInternal(preferences, key);
245+
WrappedKeyTrackingPreferences wrappedPref = new WrappedKeyTrackingPreferences(preferences);
246+
storeInternal(wrappedPref, key);
247+
return wrappedPref.getKeysByType();
244248
}
245249

246250
private void loadInternal(Preferences preferences, String prefix) {
@@ -282,4 +286,152 @@ public boolean equals(Object other) {
282286
public int hashCode() {
283287
return Objects.hash(booleans, bytes, integers, longs, shorts, strings, children);
284288
}
289+
290+
private static class WrappedKeyTrackingPreferences implements Preferences {
291+
private final Preferences preferences;
292+
private final Map<PreferenceType, Set<String>> keysByType = new HashMap<>();
293+
294+
public WrappedKeyTrackingPreferences(Preferences preferences) {
295+
this.preferences = preferences;
296+
for (var type : PreferenceType.values()) {
297+
keysByType.put(type, new HashSet<>());
298+
}
299+
}
300+
301+
public Map<PreferenceType, Set<String>> getKeysByType() {
302+
return keysByType;
303+
}
304+
305+
@Override
306+
public String getString(String s) {
307+
return preferences.getString(s);
308+
}
309+
310+
@Override
311+
public void setString(String s, String s1) {
312+
keysByType.get(PreferenceType.STRING).add(s);
313+
preferences.setString(s, s1);
314+
}
315+
316+
@Override
317+
public void deleteString(String s) {
318+
keysByType.get(PreferenceType.STRING).remove(s);
319+
preferences.deleteString(s);
320+
}
321+
322+
@Override
323+
public Set<String> stringKeys() {
324+
return preferences.stringKeys();
325+
}
326+
327+
@Override
328+
public Boolean getBoolean(String s) {
329+
return preferences.getBoolean(s);
330+
}
331+
332+
@Override
333+
public void setBoolean(String s, boolean b) {
334+
keysByType.get(PreferenceType.BOOLEAN).add(s);
335+
preferences.setBoolean(s, b);
336+
}
337+
338+
@Override
339+
public void deleteBoolean(String s) {
340+
keysByType.get(PreferenceType.BOOLEAN).remove(s);
341+
preferences.deleteBoolean(s);
342+
}
343+
344+
@Override
345+
public Set<String> booleanKeys() {
346+
return preferences.booleanKeys();
347+
}
348+
349+
@Override
350+
public Byte getByte(String s) {
351+
return preferences.getByte(s);
352+
}
353+
354+
@Override
355+
public void setByte(String s, byte b) {
356+
keysByType.get(PreferenceType.BYTE).add(s);
357+
preferences.setByte(s, b);
358+
}
359+
360+
@Override
361+
public void deleteByte(String s) {
362+
keysByType.get(PreferenceType.BYTE).remove(s);
363+
preferences.deleteByte(s);
364+
}
365+
366+
@Override
367+
public Set<String> byteKeys() {
368+
return preferences.byteKeys();
369+
}
370+
371+
@Override
372+
public Short getShort(String s) {
373+
return preferences.getShort(s);
374+
}
375+
376+
@Override
377+
public void setShort(String s, short i) {
378+
keysByType.get(PreferenceType.SHORT).add(s);
379+
preferences.setShort(s, i);
380+
}
381+
382+
@Override
383+
public void deleteShort(String s) {
384+
keysByType.get(PreferenceType.SHORT).remove(s);
385+
preferences.deleteShort(s);
386+
}
387+
388+
@Override
389+
public Set<String> shortKeys() {
390+
return preferences.shortKeys();
391+
}
392+
393+
@Override
394+
public Integer getInteger(String s) {
395+
return preferences.getInteger(s);
396+
}
397+
398+
@Override
399+
public void setInteger(String s, int i) {
400+
keysByType.get(PreferenceType.INTEGER).add(s);
401+
preferences.setInteger(s, i);
402+
}
403+
404+
@Override
405+
public void deleteInteger(String s) {
406+
keysByType.get(PreferenceType.INTEGER).remove(s);
407+
preferences.deleteInteger(s);
408+
}
409+
410+
@Override
411+
public Set<String> integerKeys() {
412+
return preferences.integerKeys();
413+
}
414+
415+
@Override
416+
public Long getLong(String s) {
417+
return preferences.getLong(s);
418+
}
419+
420+
@Override
421+
public void setLong(String s, long l) {
422+
keysByType.get(PreferenceType.LONG).add(s);
423+
preferences.setLong(s, l);
424+
}
425+
426+
@Override
427+
public void deleteLong(String s) {
428+
keysByType.get(PreferenceType.LONG).remove(s);
429+
preferences.deleteLong(s);
430+
}
431+
432+
@Override
433+
public Set<String> longKeys() {
434+
return preferences.longKeys();
435+
}
436+
}
285437
}

0 commit comments

Comments
 (0)