/** * Sends a serializable object. */ public void writeObject(Object o) throws IOException { ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(out); oos.writeObject(o); // don't close oss, which will close the underlying stream // no need to flush either, given the way oos is implemented }
@Override public boolean isBlacklisted(Class c) { AnonymousClassWarnings.check(c); return false; }
private static void doCheck(@Nonnull Class<?> c) { if (Enum.class.isAssignableFrom(c)) { // e.g., com.cloudbees.plugins.credentials.CredentialsScope$1 ~ CredentialsScope.SYSTEM // ignore, enums serialize specially } else if (c.isAnonymousClass()) { // e.g., pkg.Outer$1 warn(c, "anonymous"); } else if (c.isLocalClass()) { // e.g., pkg.Outer$1Local warn(c, "local"); } else if (c.isSynthetic()) { // e.g., pkg.Outer$$Lambda$1/12345678 warn(c, "synthetic"); } }
private static void warn(@Nonnull Class<?> c, String kind) { String name = c.getName(); String codeSource = codeSource(c); // Need to be very defensive about calling anything while holding this lock, lest we trigger class loading-related deadlocks. boolean doWarn; synchronized (checked) { doWarn = checked.put(c, true) == null; } if (doWarn) { if (codeSource == null) { LOGGER.warning("Attempt to (de-)serialize " + kind + " class " + name + "; see: https://jenkins.io/redirect/serialization-of-anonymous-classes/"); } else { // most easily tracked back to source using javap -classpath <location> -l '<name>' LOGGER.warning("Attempt to (de-)serialize " + kind + " class " + name + " in " + codeSource + "; see: https://jenkins.io/redirect/serialization-of-anonymous-classes/"); } } }
/** * Checks a class which is being either serialized or deserialized. * A warning will only be printed once per class per JVM session. */ public static void check(@Nonnull Class<?> clazz) { synchronized (checked) { if (checked.containsKey(clazz)) { return; // fast path } } Channel channel = Channel.current(); if (channel == null) { doCheck(clazz); } else { // May not call methods like Class#isAnonymousClass synchronously, since these can in turn trigger remote class loading. try { channel.executor.submit((Runnable) () -> doCheck(clazz)); } catch (RejectedExecutionException x) { // never mind, we tried } } }
@Override protected void annotateClass(Class<?> c) throws IOException { check(c); super.annotateClass(c); } };
public long writeHtmlTo(long start, Writer w) throws IOException { ConsoleAnnotationOutputStream<T> caw = new ConsoleAnnotationOutputStream<>( w, createAnnotator(Stapler.getCurrentRequest()), context, charset); long r = super.writeLogTo(start,caw); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Cipher sym = PASSING_ANNOTATOR.encrypt(); ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(new GZIPOutputStream(new CipherOutputStream(baos,sym))); oos.writeLong(System.currentTimeMillis()); // send timestamp to prevent a replay attack oos.writeObject(caw.getConsoleAnnotator()); oos.close(); StaplerResponse rsp = Stapler.getCurrentResponse(); if (rsp!=null) rsp.setHeader("X-ConsoleAnnotator", new String(Base64.encode(baos.toByteArray()))); return r; }
@Override protected void annotateClass(Class<?> c) throws IOException { AnonymousClassWarnings.check(c); super.annotateClass(c); } }) {
private ByteArrayOutputStream encodeToBytes() throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); try (OutputStream gzos = new GZIPOutputStream(buf); ObjectOutputStream oos = JenkinsJVM.isJenkinsJVM() ? AnonymousClassWarnings.checkingObjectOutputStream(gzos) : new ObjectOutputStream(gzos)) { oos.writeObject(this); } ByteArrayOutputStream buf2 = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(new Base64OutputStream(buf2,true,-1,null)); try { buf2.write(PREAMBLE); if (JenkinsJVM.isJenkinsJVM()) { // else we are in another JVM and cannot sign; result will be ignored unless INSECURE byte[] mac = MAC.mac(buf.toByteArray()); dos.writeInt(- mac.length); // negative to differentiate from older form dos.write(mac); } dos.writeInt(buf.size()); buf.writeTo(dos); } finally { dos.close(); } buf2.write(POSTAMBLE); return buf2; }
@Override protected void annotateClass(Class<?> c) throws IOException { AnonymousClassWarnings.check(c); ClassLoader cl = c.getClassLoader(); if (cl==null) {// bootstrap classloader. no need to export. writeInt(TAG_SYSTEMCLASSLOADER); return; } Integer idx = classLoaders.get(cl); if (idx==null) { classLoaders.put(cl,classLoaders.size()); if (cl instanceof RemoteClassLoader) { int oid = ((RemoteClassLoader) cl).getOid(channel); if (oid>=0) { // this classloader came from where we are sending this classloader to. writeInt(TAG_LOCAL_CLASSLOADER); writeInt(oid); return; } } // tell the receiving side that they need to import a new classloader // this reference count is released when RemoteInvocationHandler backing IClassLoader is GCed on the remote node. writeInt(TAG_EXPORTED_CLASSLOADER); writeInt(RemoteClassLoader.exportId(cl,channel)); } else {// reference to a classloader that's already written writeInt(idx); } }
@Override public final void write(Command cmd, boolean last) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(baos); cmd.writeTo(channel,oos); oos.close(); byte[] block = baos.toByteArray(); channel.notifyWrite(cmd, block.length); writeBlock(channel, block); }
@Override public void write(Command cmd, boolean last) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(baos); cmd.writeTo(channel,oos); oos.close(); byte[] block = baos.toByteArray(); channel.notifyWrite(cmd, block.length); writeBlock(channel, block); } }
private byte[] _serialize(Object o, final Channel channel) throws IOException { Channel old = Channel.setCurrent(channel); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos; if (channel.remoteCapability.supportsMultiClassLoaderRPC()) oos = new MultiClassLoaderSerializer.Output(channel,baos); else oos = AnonymousClassWarnings.checkingObjectOutputStream(baos); oos.writeObject(o); return baos.toByteArray(); } finally { Channel.setCurrent(old); } }
/** * {@inheritDoc} */ @Override public void start() throws IOException { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(BinarySafeStream.wrap(bos)); try { oos.writeObject(new Capability()); } finally { oos.close(); } ByteBuffer buffer = ByteBufferUtils.wrapUTF8(bos.toString("US-ASCII")); write(buffer); } catch (IOException e) { futureChannel.setException(e); } }
/** * {@inheritDoc} */ @Override public final void write(Command cmd, boolean last) throws IOException { ByteBufferQueueOutputStream bqos = new ByteBufferQueueOutputStream(sendStaging); ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(bqos); try { cmd.writeTo(channel, oos); } finally { oos.close(); } long remaining = sendStaging.remaining(); channel.notifyWrite(cmd, remaining); while (remaining > 0L) { int frame = remaining > transportFrameSize ? transportFrameSize : (int) remaining; // # of bytes we send in this chunk writeChunkHeader.clear(); ChunkHeader.write(writeChunkHeader, frame, remaining > transportFrameSize); writeChunkHeader.flip(); writeChunkBody.clear(); writeChunkBody.limit(frame); sendStaging.get(writeChunkBody); writeChunkBody.flip(); write(writeChunkHeader, writeChunkBody); remaining -= frame; } }
/** * Instantiate a transport. * * @param is * The negotiated input stream that hides * @param os * {@linkplain CommandTransport#getUnderlyingStream() the underlying stream}. * @param mode * The mode to create the transport in. * @param cap * Capabilities of the other side, as determined during the handshaking. */ protected CommandTransport makeTransport(InputStream is, OutputStream os, Mode mode, Capability cap) throws IOException { FlightRecorderInputStream fis = new FlightRecorderInputStream(is); if (cap.supportsChunking()) return new ChunkedCommandTransport(cap, mode.wrap(fis), mode.wrap(os), os); else { ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(mode.wrap(os)); oos.flush(); // make sure that stream preamble is sent to the other end. avoids dead-lock return new ClassicCommandTransport( new ObjectInputStreamEx(mode.wrap(fis),getBaseLoader(),getClassFilter()), oos,fis,os,cap); } } }