Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@
@SubtypeOf(UnknownConfidential.class)
@QualifierForLiterals({LiteralKind.STRING, LiteralKind.PRIMITIVE})
@DefaultQualifierInHierarchy
@DefaultFor(value = {TypeUseLocation.LOCAL_VARIABLE, TypeUseLocation.UPPER_BOUND})
@DefaultFor(value = {TypeUseLocation.LOCAL_VARIABLE})
public @interface NonConfidential {}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package org.checkerframework.checker.confidential;

import com.sun.source.tree.MethodInvocationTree;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import org.checkerframework.checker.confidential.qual.BottomConfidential;
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;
Expand All @@ -14,13 +12,8 @@
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFTransfer;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.TreeUtils;

/** Annotated type factory for the Confidential Checker. */
public class ConfidentialAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
Expand Down Expand Up @@ -56,10 +49,6 @@ public class ConfidentialAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
/** A singleton set containing the {@code @}{@link NonConfidential} annotation mirror. */
private final AnnotationMirrorSet setOfNonConfidential;

/** The Object.toString method. */
private final ExecutableElement objectToString =
TreeUtils.getMethod("java.lang.Object", "toString", 0, processingEnv);

/**
* Creates a {@link ConfidentialAnnotatedTypeFactory}.
*
Expand All @@ -78,64 +67,14 @@ public ConfidentialAnnotatedTypeFactory(BaseTypeChecker checker) {
postInit();
}

@Override
protected Set<AnnotationMirror> getEnumConstructorQualifiers() {
return setOfNonConfidential;
}

@Override
public TreeAnnotator createTreeAnnotator() {
return new ListTreeAnnotator(
super.createTreeAnnotator(),
new ConfidentialAnnotatedTypeFactory.ConfidentialTreeAnnotator(this));
}

@Override
public CFTransfer createFlowTransferFunction(
CFAbstractAnalysis<CFValue, CFStore, CFTransfer> analysis) {
return new ConfidentialTransfer(analysis);
}

/**
* A TreeAnnotator to enforce certain toString return type rules:
*
* <ul>
* <li>toString(@NonConfidential this) can return @NonConfidential or @Confidential String
* <li>toString(@Confidential this) can return @Confidential String
* </ul>
*/
private class ConfidentialTreeAnnotator extends TreeAnnotator {
/**
* Creates a {@link ConfidentialAnnotatedTypeFactory.ConfidentialTreeAnnotator}
*
* @param atypeFactory the annotated type factory
*/
public ConfidentialTreeAnnotator(AnnotatedTypeFactory atypeFactory) {
super(atypeFactory);
}

/**
* Visits a method invocation node. Enforces specific type-checking rules for Object.toString()
* that allow a @NonConfidential Object to return a @NonConfidential String.
*
* <p>Supplements the @Confidential String return in Object.toString() to cover all secure use
* cases, i.e. all cases covered by a @PolyConfidential receiver and return excepting
* a @NonConfidential String from @Confidential receivers.
*
* @param tree an AST node representing a method call
* @param type the type obtained from tree
*/
@Override
public Void visitMethodInvocation(MethodInvocationTree tree, AnnotatedTypeMirror type) {
if (TreeUtils.isMethodInvocation(tree, objectToString, processingEnv)) {
AnnotatedTypeMirror receiver = getReceiverType(tree);
if (receiver.hasPrimaryAnnotation(NONCONFIDENTIAL)) {
type.replaceAnnotation(NONCONFIDENTIAL);
} else {
type.replaceAnnotation(CONFIDENTIAL);
}
}
return super.visitMethodInvocation(tree, type);
}
@Override
protected Set<AnnotationMirror> getEnumConstructorQualifiers() {
return setOfNonConfidential;
}
}
58 changes: 58 additions & 0 deletions checker/tests/confidential/ConfidentialArithmetic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;

public class ConfidentialArithmetic {
void addition(@Confidential int confInt, @NonConfidential int nonConfInt) {
@Confidential int r1 = confInt + nonConfInt;
@Confidential int r2 = confInt + confInt;
@NonConfidential int r3 = nonConfInt + nonConfInt;

// :: error: [assignment]
@NonConfidential int r4 = nonConfInt + confInt;
// :: error: [assignment]
@NonConfidential int r5 = confInt + nonConfInt;
// :: error: [assignment]
@NonConfidential int r6 = confInt + confInt;
}

void subtraction(@Confidential int confInt, @NonConfidential int nonConfInt) {
@Confidential int r1 = confInt - nonConfInt;
@NonConfidential int r2 = nonConfInt - nonConfInt;

// :: error: [assignment]
@NonConfidential int r3 = confInt - confInt;
// :: error: [assignment]
@NonConfidential int r4 = confInt - nonConfInt;
// :: error: [assignment]
@NonConfidential int r5 = nonConfInt - confInt;
}

void compoundAssignment(@Confidential int confInt, @NonConfidential int nonConfInt) {
@NonConfidential int x = 0;
x += nonConfInt;

@Confidential int y = 0;
y += confInt;
y += nonConfInt;
}

void comparison(@Confidential int confInt, @NonConfidential int nonConfInt) {
// :: error: [assignment]
@NonConfidential boolean b1 = confInt > nonConfInt;
// :: error: [assignment]
@NonConfidential boolean b2 = confInt == nonConfInt;
@NonConfidential boolean b3 = nonConfInt < nonConfInt;
}

void intStringConcatenation(
@Confidential int confInt,
@NonConfidential int nonConfInt,
@Confidential String confStr,
@NonConfidential String nonConfStr) {
@Confidential String s1 = confStr + confInt;
@NonConfidential String s2 = nonConfStr + nonConfInt;

// :: error: [assignment]
@NonConfidential String s3 = nonConfStr + confInt;
}
}
69 changes: 69 additions & 0 deletions checker/tests/confidential/ConfidentialAssignment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;

public class ConfidentialAssignment {
void stringAssignments(@Confidential String conf, @NonConfidential String nonConf) {
@Confidential String s1 = nonConf;
@Confidential String s3 = conf;
@NonConfidential String s4 = nonConf;

// :: error: [assignment]
@NonConfidential String s2 = conf;
}

void literalDefaults() {
// Literals default to @NonConfidential
@NonConfidential String s = "hello";
@Confidential String cs = "hello";
}

void intAssignments(@Confidential int confInt, @NonConfidential int nonConfInt) {
@Confidential int i1 = nonConfInt;
@Confidential int i3 = confInt;
@NonConfidential int i4 = nonConfInt;

// :: error: [assignment]
@NonConfidential int i2 = confInt;
}

void integerAssignments(@Confidential Integer confInt, @NonConfidential Integer nonConfInt) {
@Confidential Integer i1 = nonConfInt;
@Confidential Integer i3 = confInt;
@NonConfidential Integer i4 = nonConfInt;

// :: error: [assignment]
@NonConfidential Integer i2 = confInt;
}

void boxing(@Confidential int confInt, @NonConfidential int nonConfInt) {
@Confidential Integer boxed1 = confInt;
@NonConfidential Integer boxed2 = nonConfInt;

// :: error: [assignment]
@NonConfidential Integer boxed3 = confInt;
}

void unboxing(@Confidential Integer confInteger, @NonConfidential Integer nonConfInteger) {
@Confidential int unboxed1 = confInteger;
@NonConfidential int unboxed2 = nonConfInteger;

// :: error: [assignment]
@NonConfidential int unboxed3 = confInteger;
}

void intLiteralDefaults() {
@NonConfidential int i = 42;
@Confidential int ci = 42;
@NonConfidential Integer bi = 42;
@Confidential Integer cbi = 42;
}

void reassignment(@Confidential String conf, @NonConfidential String nonConf) {
@Confidential String s = nonConf;
s = conf;

@NonConfidential String s2 = nonConf;
// :: error: [assignment]
s2 = conf;
}
}
38 changes: 38 additions & 0 deletions checker/tests/confidential/ConfidentialCasting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;

public class ConfidentialCasting {
void castToConfidential(@NonConfidential String nonConf, @NonConfidential int nonConfInt) {
// :: warning: [cast.unsafe]
@Confidential String s = (@Confidential String) nonConf;
// :: warning: [cast.unsafe]
@Confidential int i = (@Confidential int) nonConfInt;
}

void castConfidentialToConfidential(@Confidential String conf) {
@Confidential String s = (@Confidential String) conf;
}

void castToNonConfidential(@Confidential String conf, @Confidential int confInt) {
// :: error: [assignment] :: warning: [cast.unsafe]
@NonConfidential String s = (@NonConfidential String) conf;
// :: error: [assignment] :: warning: [cast.unsafe]
@NonConfidential int i = (@NonConfidential int) confInt;
}

void numericWidening(@Confidential int confInt, @NonConfidential int nonConfInt) {
@Confidential long cl = confInt;
@NonConfidential long ncl = nonConfInt;

// :: error: [assignment]
@NonConfidential long ncl2 = confInt;
}

void numericNarrowing(@Confidential long confLong, @NonConfidential long nonConfLong) {
@Confidential int ci = (int) confLong;
@NonConfidential int nci = (int) nonConfLong;

// :: error: [assignment]
@NonConfidential int nci2 = (int) confLong;
}
}
42 changes: 42 additions & 0 deletions checker/tests/confidential/ConfidentialControlFlow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;

public class ConfidentialControlFlow {
void ternary(boolean flag, @Confidential String conf, @NonConfidential String nonConf) {
@NonConfidential String s1 = flag ? nonConf : nonConf;
@Confidential String s2 = flag ? conf : conf;
@Confidential String s3 = flag ? conf : nonConf;
@Confidential String s4 = flag ? nonConf : conf;

// :: error: [assignment]
@NonConfidential String s5 = flag ? conf : nonConf;
// :: error: [assignment]
@NonConfidential String s6 = flag ? nonConf : conf;
}

void ternaryInt(boolean flag, @Confidential int conf, @NonConfidential int nonConf) {
@NonConfidential int i1 = flag ? nonConf : nonConf;
@Confidential int i2 = flag ? conf : nonConf;

// :: error: [assignment]
@NonConfidential int i3 = flag ? conf : nonConf;
}

void ifElseFlow(boolean flag, @Confidential String conf, @NonConfidential String nonConf) {
@Confidential String confRes;
if (flag) {
confRes = conf;
} else {
confRes = nonConf;
}
}

void ternaryAsArg(boolean flag, @Confidential String conf, @NonConfidential String nonConf) {
sinkNonConf(flag ? nonConf : nonConf);

// :: error: [argument]
sinkNonConf(flag ? conf : nonConf);
}

void sinkNonConf(@NonConfidential String nonConf) {}
}
62 changes: 62 additions & 0 deletions checker/tests/confidential/ConfidentialGenerics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.checkerframework.checker.confidential.qual.Confidential;
import org.checkerframework.checker.confidential.qual.NonConfidential;

public class ConfidentialGenerics {
void testConfidentialMap() {
Map<String, @Confidential String> map = new HashMap<>();
@Confidential String secret = "password";
map.put("key", secret);

@Confidential String retrieved = map.get("key");

// :: error: [assignment]
@NonConfidential String leaked = map.get("key");
}

void testNonConfidentialMap() {
Map<String, String> map = new HashMap<>();
@Confidential String secret = "password";

// :: error: [argument]
map.put("key", secret);

@NonConfidential String safe = map.get("key");
}

void testConfidentialSet() {
Set<@Confidential String> set = new HashSet<>();
@Confidential String secret = "token";
set.add(secret);
}

void testNonConfidentialSet() {
Set<String> set = new HashSet<>();
@Confidential String secret = "token";

// :: error: [argument]
set.add(secret);
}

void testConfidentialList() {
List<@Confidential String> list = new ArrayList<>();
@Confidential String secret = "ssn";
list.add(secret);

@Confidential String retrieved = list.get(0);

// :: error: [assignment]
@NonConfidential String leaked = list.get(0);
}

void testConfidentialKeys() {
Map<@Confidential String, String> map = new HashMap<>();
@Confidential String confKey = "secret-key";
map.put(confKey, "value");
}
}
12 changes: 12 additions & 0 deletions checker/tests/confidential/ConfidentialHardCodeLiteral.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package confidential;

import org.checkerframework.checker.confidential.qual.Confidential;

public class ConfidentialHardCodeLiteral {
void hardCodedLiteral() {
// This should fail to prevent leak in the VCS
@Confidential String secret = "secret_key";
@Confidential int pan = 123456;
@Confidential Integer dob = 123456;
}
}
Loading