mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
Assembly to include tests in jar (#104)
Assembly to include tests in jar
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ before_install:
|
||||
|
||||
after_script:
|
||||
- > # print more detailed info about test results
|
||||
cat target/surefire-reports/TEST-ysoserial.payloads.PayloadsTest.xml |
|
||||
cat target/surefire-reports/TEST-ysoserial.test.payloads.PayloadsTest.xml |
|
||||
grep testcase -A1 | grep -B1 -E 'failure|error|skipped' | grep -v -- --
|
||||
|
||||
matrix:
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||
<id>fat-tests</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<unpack>true</unpack>
|
||||
<scope>test</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/test-classes</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*.class</include>
|
||||
</includes>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -39,10 +39,8 @@
|
||||
<mainClass>ysoserial.GeneratePayload</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
@@ -115,7 +113,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jboss.shrinkwrap.resolver</groupId>
|
||||
<artifactId>shrinkwrap-resolver-depchain</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<version>2.2.6</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -303,6 +301,11 @@
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.clojure</groupId>
|
||||
<artifactId>clojure</artifactId>
|
||||
|
||||
@@ -38,7 +38,7 @@ import ysoserial.payloads.util.Reflections;
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
@PayloadTest ( harness = "ysoserial.payloads.RemoteClassLoadingTest" )
|
||||
@PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" )
|
||||
@Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} )
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class C3P0 implements ObjectPayload<Object> {
|
||||
|
||||
@@ -49,7 +49,6 @@ This only works in JDK 8u76 and WITHOUT a security manager
|
||||
|
||||
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
|
||||
*/
|
||||
//@PayloadTest(skip="need more robust way to detect Runtime.exec() without SecurityManager()")
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@PayloadTest ( precondition = "isApplicableJavaVersion")
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
|
||||
@@ -41,7 +41,7 @@ import ysoserial.payloads.util.Reflections;
|
||||
"commons-fileupload:commons-fileupload:1.3.1",
|
||||
"commons-io:commons-io:2.4"
|
||||
} )
|
||||
@PayloadTest(harness="ysoserial.payloads.FileUploadTest", precondition = "isApplicableJavaVersion")
|
||||
@PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", precondition = "isApplicableJavaVersion")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class FileUpload1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
|
||||
@@ -42,7 +42,7 @@ import com.sun.rowset.JdbcRowSetImpl;
|
||||
@SuppressWarnings ( {
|
||||
"restriction"
|
||||
} )
|
||||
@PayloadTest(harness = "ysoserial.payloads.JRMPReverseConnectTest", precondition = "isApplicableJavaVersion")
|
||||
@PayloadTest(harness="ysoserial.test.payloads.JRMPReverseConnectTest", precondition = "isApplicableJavaVersion")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class Hibernate2 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
|
||||
@@ -49,7 +49,7 @@ import ysoserial.payloads.util.PayloadRunner;
|
||||
@SuppressWarnings ( {
|
||||
"restriction"
|
||||
} )
|
||||
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
|
||||
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import ysoserial.payloads.util.PayloadRunner;
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@PayloadTest(harness = "ysoserial.payloads.MyfacesTest", precondition = "isApplicableJavaVersion")
|
||||
@PayloadTest(harness="ysoserial.test.payloads.MyfacesTest", precondition = "isApplicableJavaVersion")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class Myfaces2 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
|
||||
@@ -46,7 +46,7 @@ import ysoserial.payloads.util.Reflections;
|
||||
* $ cat /tmp/upload_3805815b_2d50_4e00_9dae_a854d5a0e614_479431761.tmp
|
||||
* blue lobster
|
||||
*/
|
||||
@PayloadTest(harness = "ysoserial.payloads.FileUploadTest", flaky="possible race condition")
|
||||
@PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", flaky="possible race condition")
|
||||
@Dependencies({"org.apache.wicket:wicket-util:6.23.0", "org.slf4j:slf4j-api:1.6.4"})
|
||||
@Authors({ Authors.JACOBAINES })
|
||||
public class Wicket1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial;
|
||||
package ysoserial.test;
|
||||
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial;
|
||||
package ysoserial.test;
|
||||
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial;
|
||||
package ysoserial.test;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial;
|
||||
package ysoserial.test;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial.exploit;
|
||||
package ysoserial.test.exploit;
|
||||
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
import org.junit.Assert;
|
||||
import ysoserial.CustomTest;
|
||||
import ysoserial.util.Files;
|
||||
import ysoserial.util.OS;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.test.util.Files;
|
||||
import ysoserial.test.util.OS;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -9,8 +9,8 @@ import org.junit.Assert;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import ysoserial.CustomTest;
|
||||
import ysoserial.util.OS;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.test.util.OS;
|
||||
|
||||
/**
|
||||
* @author mbechler
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import ysoserial.WrappedTest;
|
||||
import ysoserial.test.WrappedTest;
|
||||
import ysoserial.exploit.JRMPListener;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class JRMPReverseConnectSMTest extends RemoteClassLoadingTest implements
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see ysoserial.payloads.RemoteClassLoadingTest#createCallable(java.util.concurrent.Callable)
|
||||
* @see RemoteClassLoadingTest#createCallable(java.util.concurrent.Callable)
|
||||
*/
|
||||
@Override
|
||||
public Callable<Object> createCallable ( final Callable<Object> innerCallable ) {
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -7,7 +7,7 @@ import javax.management.BadAttributeValueExpException;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import ysoserial.CustomTest;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.exploit.JRMPListener;
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
|
||||
import java.beans.FeatureDescriptor;
|
||||
@@ -23,7 +23,7 @@ import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ysoserial.CustomDeserializer;
|
||||
import ysoserial.test.CustomDeserializer;
|
||||
import ysoserial.Deserializer;
|
||||
|
||||
|
||||
+125
-16
@@ -1,32 +1,44 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.JUnitCore;
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import ysoserial.*;
|
||||
import ysoserial.util.Throwables;
|
||||
import ysoserial.payloads.TestHarnessTest.ExecMockPayload;
|
||||
import ysoserial.payloads.TestHarnessTest.NoopMockPayload;
|
||||
import ysoserial.payloads.DynamicDependencies;
|
||||
import ysoserial.payloads.ObjectPayload;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.test.CustomDeserializer;
|
||||
import ysoserial.test.CustomPayloadArgs;
|
||||
import ysoserial.test.WrappedTest;
|
||||
import ysoserial.test.payloads.TestHarnessTest.ExecMockPayload;
|
||||
import ysoserial.test.payloads.TestHarnessTest.NoopMockPayload;
|
||||
import ysoserial.payloads.annotation.Dependencies;
|
||||
import ysoserial.payloads.annotation.PayloadTest;
|
||||
import ysoserial.payloads.util.ClassFiles;
|
||||
import ysoserial.secmgr.ExecCheckingSecurityManager;
|
||||
import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException;
|
||||
|
||||
|
||||
/*
|
||||
@@ -65,18 +77,22 @@ public class PayloadsTest {
|
||||
|
||||
public static void testPayload ( final Class<? extends ObjectPayload<?>> payloadClass, final Class<?>[] addlClassesForClassLoader )
|
||||
throws Exception {
|
||||
System.out.println("Testing payload: " + payloadClass.getName());
|
||||
|
||||
String command = "hostname";
|
||||
|
||||
PayloadTest t = payloadClass.getAnnotation(PayloadTest.class);
|
||||
|
||||
int tries = 1;
|
||||
if ( t != null ) {
|
||||
if ( !t.skip().isEmpty() ) {
|
||||
Assume.assumeTrue(t.skip(), false);
|
||||
}
|
||||
if (System.getProperty("forceTests") == null) {
|
||||
if ( !t.skip().isEmpty() ) {
|
||||
Assume.assumeTrue(t.skip(), false);
|
||||
}
|
||||
|
||||
if ( !t.precondition().isEmpty() ) {
|
||||
Assume.assumeTrue("Precondition: " + t.precondition(), checkPrecondition(payloadClass, t.precondition()));
|
||||
if ( !t.precondition().isEmpty() ) {
|
||||
Assume.assumeTrue("Precondition: " + t.precondition(), checkPrecondition(payloadClass, t.precondition()));
|
||||
}
|
||||
}
|
||||
|
||||
if (! t.flaky().isEmpty()) {
|
||||
@@ -99,18 +115,18 @@ public class PayloadsTest {
|
||||
testHarness = new CommandExecTest(); // default
|
||||
}
|
||||
|
||||
if ( testHarness instanceof CustomPayloadArgs ) {
|
||||
if ( testHarness instanceof CustomPayloadArgs) {
|
||||
payloadCommand = ( (CustomPayloadArgs) testHarness ).getPayloadArgs();
|
||||
}
|
||||
|
||||
if ( testHarness instanceof CustomDeserializer ) {
|
||||
if ( testHarness instanceof CustomDeserializer) {
|
||||
customDeserializer = ((CustomDeserializer)testHarness).getCustomDeserializer();
|
||||
}
|
||||
|
||||
// TODO per-thread secmgr to enforce no detonation during deserialization
|
||||
final byte[] serialized = makeSerializeCallable(payloadClass, payloadCommand).call();
|
||||
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized, customDeserializer);
|
||||
if ( testHarness instanceof WrappedTest ) {
|
||||
if ( testHarness instanceof WrappedTest) {
|
||||
callable = ( (WrappedTest) testHarness ).createCallable(callable);
|
||||
}
|
||||
|
||||
@@ -184,7 +200,12 @@ public class PayloadsTest {
|
||||
|
||||
static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class<?>[] classDependencies, final Class<?> customDeserializer )
|
||||
throws Exception {
|
||||
File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0];
|
||||
File[] jars = dependencies.length > 0
|
||||
? Maven.configureResolver()
|
||||
.withMavenCentralRepo(true)
|
||||
.withRemoteRepo("jenkins", "http://repo.jenkins-ci.org/public/", "default")
|
||||
.resolve(dependencies).withoutTransitivity().asFile()
|
||||
: new File[0];
|
||||
URL[] urls = new URL[jars.length];
|
||||
for ( int i = 0; i < jars.length; i++ ) {
|
||||
urls[ i ] = jars[ i ].toURI().toURL();
|
||||
@@ -230,4 +251,92 @@ public class PayloadsTest {
|
||||
Thread.currentThread().setContextClassLoader(ccl);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
JUnitCore junit = new JUnitCore();
|
||||
PayloadListener listener = new PayloadListener();
|
||||
junit.addListener(listener);
|
||||
Result result = junit.run(PayloadsTest.class);
|
||||
System.exit(result.wasSuccessful() ? 0 : 1);
|
||||
}
|
||||
|
||||
public static class StdIo {
|
||||
|
||||
private static final PrintStream realOut = System.out;
|
||||
private static final PrintStream realErr = System.err;
|
||||
|
||||
public static void restoreStreams() {
|
||||
setStreams(realOut, realErr);
|
||||
}
|
||||
|
||||
public static void setStreams(PrintStream out, PrintStream err) {
|
||||
System.setOut(out);
|
||||
System.setErr(err);
|
||||
}
|
||||
|
||||
public static void setStreams(OutputStream out, OutputStream err) {
|
||||
setStreams(new PrintStream(out), new PrintStream(err));
|
||||
}
|
||||
}
|
||||
|
||||
public static class PayloadListener extends RunListener {
|
||||
public enum Status {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
IGNORE,
|
||||
ASSUMPTION_FAILURE
|
||||
}
|
||||
|
||||
private Map<Description, ByteArrayOutputStream> outs = new HashMap<Description, ByteArrayOutputStream>();
|
||||
private Map<Description, ByteArrayOutputStream> errs = new HashMap<Description, ByteArrayOutputStream>();
|
||||
|
||||
private Map<Description, Status> statuses = new HashMap<Description, Status>();
|
||||
|
||||
private Map<Description, Failure> failures = new HashMap<Description, Failure>();
|
||||
|
||||
@Override
|
||||
public void testStarted(Description description) throws Exception {
|
||||
System.out.println(getPayload(description.getDisplayName()) + ": STARTED");
|
||||
|
||||
statuses.put(description, Status.SUCCESS);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// ByteArrayOutputStream err = new ByteArrayOutputStream();
|
||||
|
||||
outs.put(description, out);
|
||||
// errs.put(description, err);
|
||||
|
||||
StdIo.setStreams(out, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFinished(Description description) throws Exception {
|
||||
outs.get(description).close();
|
||||
//errs.get(description).close();
|
||||
|
||||
StdIo.restoreStreams();
|
||||
|
||||
Status status = statuses.get(description);
|
||||
System.out.println(getPayload(description.getDisplayName()) + ": " + status);
|
||||
if (status == Status.FAILURE) System.err.println(outs.get(description).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFailure(Failure failure) throws Exception {
|
||||
statuses.put(failure.getDescription(), Status.FAILURE);
|
||||
failures.put(failure.getDescription(), failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAssumptionFailure(Failure failure) {
|
||||
statuses.put(failure.getDescription(), Status.ASSUMPTION_FAILURE);
|
||||
failures.put(failure.getDescription(), failure);
|
||||
}
|
||||
|
||||
// testPayload[payloadClass: class ysoserial.payloads.JavassistWeld1](ysoserial.test.payloads.PayloadsTest)
|
||||
public static String getPayload(String displayName) {
|
||||
return displayName.replaceAll(".*\\[\\S+: class (\\w+\\.)+(\\w+)\\].*", "$2");
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -12,7 +12,7 @@ import fi.iki.elonen.NanoHTTPD.Response.Status;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import ysoserial.WrappedTest;
|
||||
import ysoserial.test.WrappedTest;
|
||||
|
||||
|
||||
/**
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial.payloads;
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
@@ -7,6 +7,7 @@ import java.io.Serializable;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import ysoserial.payloads.ObjectPayload;
|
||||
|
||||
public class TestHarnessTest {
|
||||
// make sure test harness fails properly
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial.util;
|
||||
package ysoserial.test.util;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial.util;
|
||||
package ysoserial.test.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package ysoserial.util;
|
||||
package ysoserial.test.util;
|
||||
|
||||
public enum OS {
|
||||
WINDOWS,
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ysoserial.util;
|
||||
package ysoserial.test.util;
|
||||
|
||||
public class Throwables {
|
||||
public static Throwable getInnermostCause(final Throwable t) {
|
||||
Reference in New Issue
Block a user