mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-26 21:27:40 -05:00
[MM-69810] Update golang.org/x/image dep (#37595)
This commit is contained in:
@@ -280,8 +280,11 @@ test.describe('Post height', () => {
|
||||
},
|
||||
{
|
||||
name: 'post with an SVG Markdown image',
|
||||
// TODO Either Chrome preloads the SVG's dimensions early or Firefox doesn't allocate the height properly
|
||||
skipProjects: ['firefox'],
|
||||
// Markdown/remote SVGs intentionally receive no server-provided dimensions
|
||||
// (SVG images are filtered from link metadata to mitigate the MM-67372 DoS),
|
||||
// so the client cannot reserve height before the SVG loads. That makes a
|
||||
// layout-shift-free render impossible for this case regardless of browser.
|
||||
skipProjects: ['chrome', 'firefox', 'ipad'],
|
||||
getSeedOptions: (baseUrl) => ({
|
||||
message: ``,
|
||||
}),
|
||||
|
||||
@@ -222,7 +222,8 @@ func NewChannels(s *Server) (*Channels, error) {
|
||||
decoderConcurrency = runtime.NumCPU()
|
||||
}
|
||||
ch.imgDecoder, imgErr = imaging.NewDecoder(imaging.DecoderOptions{
|
||||
ConcurrencyLevel: decoderConcurrency,
|
||||
ConcurrencyLevel: decoderConcurrency,
|
||||
MaxDecodedResolution: *ch.cfgSvc.Config().FileSettings.MaxImageResolution,
|
||||
})
|
||||
if imgErr != nil {
|
||||
return nil, errors.Wrap(imgErr, "failed to create image decoder")
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package imaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
@@ -23,6 +24,13 @@ type DecoderOptions struct {
|
||||
// The level of concurrency for the decoder. This defines a limit on the
|
||||
// number of concurrently running encoding goroutines.
|
||||
ConcurrencyLevel int
|
||||
|
||||
// MaxDecodedResolution, when greater than zero, is the maximum number of
|
||||
// pixels (width*height) an image may declare before it is decoded. Images
|
||||
// exceeding this limit are rejected up front. This is a defense-in-depth
|
||||
// guard against decompression bombs that bounds server-side memory
|
||||
// allocation regardless of the underlying codec's behavior.
|
||||
MaxDecodedResolution int64
|
||||
}
|
||||
|
||||
func (o *DecoderOptions) validate() error {
|
||||
@@ -52,8 +60,76 @@ func NewDecoder(opts DecoderOptions) (*Decoder, error) {
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// enforceResolutionLimit inspects the image header and rejects images whose
|
||||
// declared resolution exceeds the configured MaxDecodedResolution before any
|
||||
// pixel data is decoded. It returns the reader to use for the subsequent full
|
||||
// decode: seekable readers are rewound to their original position, while
|
||||
// non-seekable readers are buffered so the cap is enforced for every input.
|
||||
func (d *Decoder) enforceResolutionLimit(rd io.Reader) (io.Reader, error) {
|
||||
if d.opts.MaxDecodedResolution <= 0 {
|
||||
return rd, nil
|
||||
}
|
||||
|
||||
if seeker, ok := rd.(io.ReadSeeker); ok {
|
||||
// Preserve the caller's position so an image decoded from a non-zero
|
||||
// offset still lines up for the full decode.
|
||||
start, err := seeker.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imaging: failed to read image position: %w", err)
|
||||
}
|
||||
cfg, _, cfgErr := image.DecodeConfig(seeker)
|
||||
if _, err := seeker.Seek(start, io.SeekStart); err != nil {
|
||||
return nil, fmt.Errorf("imaging: failed to seek after reading image config: %w", err)
|
||||
}
|
||||
if err := d.checkConfigResolution(cfg, cfgErr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rd, nil
|
||||
}
|
||||
|
||||
// Non-seekable reader: buffer the input so the resolution cap can still be
|
||||
// enforced and the data can be decoded afterwards.
|
||||
data, err := io.ReadAll(rd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imaging: failed to read image data: %w", err)
|
||||
}
|
||||
cfg, _, cfgErr := image.DecodeConfig(bytes.NewReader(data))
|
||||
if err := d.checkConfigResolution(cfg, cfgErr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.NewReader(data), nil
|
||||
}
|
||||
|
||||
// checkConfigResolution rejects a decoded image config whose resolution exceeds
|
||||
// the configured cap. A config-decode error is ignored so the subsequent full
|
||||
// decode can surface a meaningful error for malformed input.
|
||||
func (d *Decoder) checkConfigResolution(cfg image.Config, cfgErr error) error {
|
||||
if cfgErr != nil {
|
||||
return nil
|
||||
}
|
||||
if exceedsResolution(int64(cfg.Width), int64(cfg.Height), d.opts.MaxDecodedResolution) {
|
||||
return fmt.Errorf("imaging: image resolution %dx%d exceeds the maximum allowed %d pixels", cfg.Width, cfg.Height, d.opts.MaxDecodedResolution)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// exceedsResolution reports whether width*height exceeds maxRes. It divides
|
||||
// instead of multiplying so it can't overflow int64 for very large declared
|
||||
// dimensions.
|
||||
func exceedsResolution(width, height, maxRes int64) bool {
|
||||
if width <= 0 || height <= 0 {
|
||||
return false
|
||||
}
|
||||
return width > maxRes/height
|
||||
}
|
||||
|
||||
// Decode decodes the given encoded data and returns the decoded image.
|
||||
func (d *Decoder) Decode(rd io.Reader) (img image.Image, format string, err error) {
|
||||
rd, err = d.enforceResolutionLimit(rd)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if d.opts.ConcurrencyLevel != 0 {
|
||||
d.sem <- struct{}{}
|
||||
defer func() { <-d.sem }()
|
||||
@@ -71,6 +147,11 @@ func (d *Decoder) Decode(rd io.Reader) (img image.Image, format string, err erro
|
||||
// must be called when access to the raw image is not needed anymore.
|
||||
// This sets the raw image data pointer to nil in an attempt to help the GC to re-use the underlying data as soon as possible.
|
||||
func (d *Decoder) DecodeMemBounded(rd io.Reader) (img image.Image, format string, releaseFunc func(), err error) {
|
||||
rd, err = d.enforceResolutionLimit(rd)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
if d.opts.ConcurrencyLevel != 0 {
|
||||
d.sem <- struct{}{}
|
||||
defer func() {
|
||||
|
||||
@@ -5,6 +5,9 @@ package imaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -256,3 +259,87 @@ func TestDecoderDecodeMemBounded(t *testing.T) {
|
||||
require.Empty(t, d.sem)
|
||||
})
|
||||
}
|
||||
|
||||
// TestDecoderMaxDecodedResolution verifies the defense-in-depth cap: the shared
|
||||
// decoder refuses to decode any image whose declared resolution exceeds the
|
||||
// configured limit, regardless of the underlying codec, before allocating
|
||||
// pixel data.
|
||||
func TestDecoderMaxDecodedResolution(t *testing.T) {
|
||||
makePNG := func(w, h int) []byte {
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, png.Encode(&buf, image.NewRGBA(image.Rect(0, 0, w, h))))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
d, err := NewDecoder(DecoderOptions{MaxDecodedResolution: 100})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Decode rejects image exceeding the cap", func(t *testing.T) {
|
||||
img, format, decErr := d.Decode(bytes.NewReader(makePNG(50, 50))) // 2500px > 100
|
||||
require.Error(t, decErr)
|
||||
require.ErrorContains(t, decErr, "exceeds the maximum allowed")
|
||||
require.Nil(t, img)
|
||||
require.Empty(t, format)
|
||||
})
|
||||
|
||||
t.Run("Decode allows image within the cap", func(t *testing.T) {
|
||||
img, format, decErr := d.Decode(bytes.NewReader(makePNG(5, 5))) // 25px <= 100
|
||||
require.NoError(t, decErr)
|
||||
require.NotNil(t, img)
|
||||
require.Equal(t, "png", format)
|
||||
})
|
||||
|
||||
t.Run("DecodeMemBounded rejects image exceeding the cap", func(t *testing.T) {
|
||||
img, format, release, decErr := d.DecodeMemBounded(bytes.NewReader(makePNG(50, 50)))
|
||||
require.Error(t, decErr)
|
||||
require.ErrorContains(t, decErr, "exceeds the maximum allowed")
|
||||
require.Nil(t, img)
|
||||
require.Empty(t, format)
|
||||
require.Nil(t, release)
|
||||
})
|
||||
|
||||
t.Run("cap disabled by default", func(t *testing.T) {
|
||||
dd, ddErr := NewDecoder(DecoderOptions{})
|
||||
require.NoError(t, ddErr)
|
||||
img, _, decErr := dd.Decode(bytes.NewReader(makePNG(50, 50)))
|
||||
require.NoError(t, decErr)
|
||||
require.NotNil(t, img)
|
||||
})
|
||||
|
||||
// A non-seekable reader must still be subject to the cap; the decoder
|
||||
// buffers it internally rather than silently bypassing the check.
|
||||
t.Run("cap enforced on non-seekable reader", func(t *testing.T) {
|
||||
// io.MultiReader is not an io.ReadSeeker.
|
||||
img, format, decErr := d.Decode(io.MultiReader(bytes.NewReader(makePNG(50, 50))))
|
||||
require.Error(t, decErr)
|
||||
require.ErrorContains(t, decErr, "exceeds the maximum allowed")
|
||||
require.Nil(t, img)
|
||||
require.Empty(t, format)
|
||||
})
|
||||
|
||||
t.Run("non-seekable reader within cap decodes from buffer", func(t *testing.T) {
|
||||
img, format, decErr := d.Decode(io.MultiReader(bytes.NewReader(makePNG(5, 5))))
|
||||
require.NoError(t, decErr)
|
||||
require.NotNil(t, img)
|
||||
require.Equal(t, "png", format)
|
||||
})
|
||||
}
|
||||
|
||||
// TestExceedsResolution verifies the resolution comparison rejects over-limit
|
||||
// images (including dimensions large enough to overflow a naive int64
|
||||
// multiplication) without wrapping around.
|
||||
func TestExceedsResolution(t *testing.T) {
|
||||
const maxRes = int64(7680 * 4320) // default 8K cap, ~33 MPx
|
||||
|
||||
require.False(t, exceedsResolution(100, 100, maxRes))
|
||||
require.False(t, exceedsResolution(7680, 4320, maxRes)) // exactly at the cap
|
||||
require.True(t, exceedsResolution(10000, 10000, maxRes))
|
||||
|
||||
// width*height here (2^80) overflows int64; the division-based check must
|
||||
// still reject it rather than wrap to a small/negative value.
|
||||
require.True(t, exceedsResolution(1<<40, 1<<40, maxRes))
|
||||
|
||||
// Non-positive dimensions are treated as not exceeding the cap.
|
||||
require.False(t, exceedsResolution(0, 100, maxRes))
|
||||
require.False(t, exceedsResolution(100, 0, maxRes))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
@@ -2354,8 +2353,10 @@ func (a *App) SetTeamIconFromMultiPartFile(rctx request.CTX, teamID string, file
|
||||
}
|
||||
|
||||
func (a *App) SetTeamIconFromFile(rctx request.CTX, team *model.Team, file io.ReadSeeker) *model.AppError {
|
||||
// Decode image into Image object
|
||||
img, format, err := image.Decode(file)
|
||||
// Decode image into Image object using the shared decoder so team icons
|
||||
// are subject to the same concurrency and resolution safeguards as other
|
||||
// user-uploaded images.
|
||||
img, format, err := a.ch.imgDecoder.Decode(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.decode.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
+5
-5
@@ -78,12 +78,12 @@ require (
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
|
||||
github.com/yuin/goldmark v1.8.2
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/image v0.40.0
|
||||
golang.org/x/image v0.44.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/sync v0.21.0
|
||||
golang.org/x/sync v0.22.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/term v0.44.0
|
||||
golang.org/x/text v0.38.0
|
||||
golang.org/x/text v0.40.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -220,8 +220,8 @@ require (
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/tools v0.47.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect
|
||||
google.golang.org/grpc v1.81.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
|
||||
+10
-10
@@ -716,8 +716,8 @@ golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsi
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw=
|
||||
golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
|
||||
golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8=
|
||||
golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
|
||||
golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -727,8 +727,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -776,8 +776,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -839,8 +839,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
@@ -856,8 +856,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -12,7 +12,6 @@ exports[`components/SizeAwareImage should load download and copy link buttons wh
|
||||
class="image-loading__container"
|
||||
>
|
||||
<img
|
||||
aria-label="file thumbnail photo-1533709752211-118fcaf03312"
|
||||
class="image-loading__placeholder class"
|
||||
height="200"
|
||||
role="presentation"
|
||||
@@ -97,7 +96,6 @@ exports[`components/SizeAwareImage should match snapshot when handleSmallImageCo
|
||||
class="image-loading__container"
|
||||
>
|
||||
<img
|
||||
aria-label="file thumbnail photo-1533709752211-118fcaf03312"
|
||||
class="image-loading__placeholder class"
|
||||
height="200"
|
||||
role="presentation"
|
||||
@@ -202,7 +200,6 @@ exports[`components/SizeAwareImage should render a placeholder and has loader wh
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
aria-label="file thumbnail photo-1533709752211-118fcaf03312"
|
||||
class="image-loading__placeholder class"
|
||||
height="200"
|
||||
role="presentation"
|
||||
|
||||
-2
@@ -12,7 +12,6 @@ exports[`components/MarkdownImage should match snapshot 1`] = `
|
||||
class="image-loading__container"
|
||||
>
|
||||
<img
|
||||
aria-label="file thumbnail"
|
||||
class="image-loading__placeholder markdown-inline-img markdown-inline-img--loading"
|
||||
height="90"
|
||||
role="presentation"
|
||||
@@ -168,7 +167,6 @@ exports[`components/MarkdownImage should match snapshot for broken link 1`] = `
|
||||
class="image-loading__container"
|
||||
>
|
||||
<img
|
||||
aria-label="file thumbnail"
|
||||
class="image-loading__placeholder markdown-inline-img markdown-inline-img--loading"
|
||||
height="10"
|
||||
role="presentation"
|
||||
|
||||
-3
@@ -61,7 +61,6 @@ exports[`components/SingleImageView permalink preview should render with permali
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
aria-label="file thumbnail name"
|
||||
class="image-loading__placeholder image-permalink"
|
||||
height="200"
|
||||
role="presentation"
|
||||
@@ -182,7 +181,6 @@ exports[`components/SingleImageView should match snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
aria-label="file thumbnail name"
|
||||
class="image-loading__placeholder"
|
||||
height="200"
|
||||
role="presentation"
|
||||
@@ -384,7 +382,6 @@ exports[`components/SingleImageView should match snapshot, SVG image 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
aria-label="file thumbnail name_svg"
|
||||
class="image-loading__placeholder"
|
||||
height="200"
|
||||
role="presentation"
|
||||
|
||||
@@ -408,11 +408,6 @@ export class SizeAwareImage extends React.PureComponent<Props, State> {
|
||||
} = this.props;
|
||||
const renderPlaceholderOnly = this.props.renderPlaceholderOnly ?? false;
|
||||
|
||||
let ariaLabelImage = this.props.intl.formatMessage({id: 'file_attachment.thumbnail', defaultMessage: 'file thumbnail'});
|
||||
if (fileInfo) {
|
||||
ariaLabelImage += ` ${fileInfo.name}`.toLowerCase();
|
||||
}
|
||||
|
||||
let fallback;
|
||||
|
||||
if (this.dimensionsAvailable(dimensions) && (!this.state.loaded || renderPlaceholderOnly)) {
|
||||
@@ -432,7 +427,6 @@ export class SizeAwareImage extends React.PureComponent<Props, State> {
|
||||
{this.renderImageLoaderIfNeeded()}
|
||||
<img
|
||||
role='presentation'
|
||||
aria-label={ariaLabelImage}
|
||||
className={classNames('image-loading__placeholder', this.props.className)}
|
||||
src={fallbackSrc}
|
||||
height={height}
|
||||
|
||||
Reference in New Issue
Block a user