mirror of
https://github.com/github/codeql.git
synced 2026-03-01 13:23:49 +01:00
Add taint-tracking for package regexp
This commit is contained in:
@@ -20,6 +20,7 @@ import semmle.go.frameworks.stdlib.PathFilepath
|
||||
import semmle.go.frameworks.stdlib.Reflect
|
||||
import semmle.go.frameworks.stdlib.Strconv
|
||||
import semmle.go.frameworks.stdlib.Strings
|
||||
import semmle.go.frameworks.stdlib.Regexp
|
||||
import semmle.go.frameworks.stdlib.TextScanner
|
||||
import semmle.go.frameworks.stdlib.TextTabwriter
|
||||
import semmle.go.frameworks.stdlib.TextTemplate
|
||||
|
||||
108
ql/src/semmle/go/frameworks/stdlib/Regexp.qll
Normal file
108
ql/src/semmle/go/frameworks/stdlib/Regexp.qll
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `regexp` package.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/** Provides models of commonly used functions in the `regexp` package. */
|
||||
module Regexp {
|
||||
private class FunctionModels extends TaintTracking::FunctionModel {
|
||||
FunctionInput inp;
|
||||
FunctionOutput outp;
|
||||
|
||||
FunctionModels() {
|
||||
// signature: func QuoteMeta(s string) string
|
||||
hasQualifiedName("regexp", "QuoteMeta") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = inp and output = outp
|
||||
}
|
||||
}
|
||||
|
||||
private class MethodModels extends TaintTracking::FunctionModel, Method {
|
||||
FunctionInput inp;
|
||||
FunctionOutput outp;
|
||||
|
||||
MethodModels() {
|
||||
// signature: func (*Regexp).Expand(dst []byte, template []byte, src []byte, match []int) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "Expand") and
|
||||
(
|
||||
inp.isParameter([1, 2]) and
|
||||
(outp.isParameter(0) or outp.isResult())
|
||||
)
|
||||
or
|
||||
// signature: func (*Regexp).ExpandString(dst []byte, template string, src string, match []int) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "ExpandString") and
|
||||
(
|
||||
inp.isParameter([1, 2]) and
|
||||
(outp.isParameter(0) or outp.isResult())
|
||||
)
|
||||
or
|
||||
// signature: func (*Regexp).Find(b []byte) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "Find") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindAll(b []byte, n int) [][]byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindAll") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindAllString(s string, n int) []string
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindAllString") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindAllStringSubmatch(s string, n int) [][]string
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindAllStringSubmatch") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindAllSubmatch(b []byte, n int) [][][]byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindAllSubmatch") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindString(s string) string
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindString") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindStringSubmatch(s string) []string
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindStringSubmatch") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).FindSubmatch(b []byte) [][]byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "FindSubmatch") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAll(src []byte, repl []byte) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAll") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAllFunc") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAllLiteral(src []byte, repl []byte) []byte
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAllLiteral") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAllLiteralString(src string, repl string) string
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAllLiteralString") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAllString(src string, repl string) string
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAllString") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).ReplaceAllStringFunc(src string, repl func(string) string) string
|
||||
this.hasQualifiedName("regexp", "Regexp", "ReplaceAllStringFunc") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
// signature: func (*Regexp).Split(s string, n int) []string
|
||||
this.hasQualifiedName("regexp", "Regexp", "Split") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = inp and output = outp
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
// Code generated by https://github.com/gagliardetto/codebox. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
import "regexp"
|
||||
|
||||
func TaintStepTest_RegexpQuoteMeta_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString656 := sourceCQL.(string)
|
||||
intoString414 := regexp.QuoteMeta(fromString656)
|
||||
return intoString414
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpand_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte518 := sourceCQL.([]byte)
|
||||
var intoByte650 []byte
|
||||
var mediumObjCQL regexp.Regexp
|
||||
mediumObjCQL.Expand(intoByte650, fromByte518, nil, nil)
|
||||
return intoByte650
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpand_B0I0O1(sourceCQL interface{}) interface{} {
|
||||
fromByte784 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte957 := mediumObjCQL.Expand(nil, fromByte784, nil, nil)
|
||||
return intoByte957
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpand_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromByte520 := sourceCQL.([]byte)
|
||||
var intoByte443 []byte
|
||||
var mediumObjCQL regexp.Regexp
|
||||
mediumObjCQL.Expand(intoByte443, nil, fromByte520, nil)
|
||||
return intoByte443
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpand_B0I1O1(sourceCQL interface{}) interface{} {
|
||||
fromByte127 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte483 := mediumObjCQL.Expand(nil, nil, fromByte127, nil)
|
||||
return intoByte483
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpandString_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString989 := sourceCQL.(string)
|
||||
var intoByte982 []byte
|
||||
var mediumObjCQL regexp.Regexp
|
||||
mediumObjCQL.ExpandString(intoByte982, fromString989, "", nil)
|
||||
return intoByte982
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpandString_B0I0O1(sourceCQL interface{}) interface{} {
|
||||
fromString417 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte584 := mediumObjCQL.ExpandString(nil, fromString417, "", nil)
|
||||
return intoByte584
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpandString_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromString991 := sourceCQL.(string)
|
||||
var intoByte881 []byte
|
||||
var mediumObjCQL regexp.Regexp
|
||||
mediumObjCQL.ExpandString(intoByte881, "", fromString991, nil)
|
||||
return intoByte881
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpExpandString_B0I1O1(sourceCQL interface{}) interface{} {
|
||||
fromString186 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte284 := mediumObjCQL.ExpandString(nil, "", fromString186, nil)
|
||||
return intoByte284
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFind_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte908 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte137 := mediumObjCQL.Find(fromByte908)
|
||||
return intoByte137
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindAll_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte494 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte873 := mediumObjCQL.FindAll(fromByte494, 0)
|
||||
return intoByte873
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindAllString_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString599 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString409 := mediumObjCQL.FindAllString(fromString599, 0)
|
||||
return intoString409
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindAllStringSubmatch_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString246 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString898 := mediumObjCQL.FindAllStringSubmatch(fromString246, 0)
|
||||
return intoString898
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindAllSubmatch_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte598 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte631 := mediumObjCQL.FindAllSubmatch(fromByte598, 0)
|
||||
return intoByte631
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindString_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString165 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString150 := mediumObjCQL.FindString(fromString165)
|
||||
return intoString150
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindStringSubmatch_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString340 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString471 := mediumObjCQL.FindStringSubmatch(fromString340)
|
||||
return intoString471
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpFindSubmatch_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte290 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte758 := mediumObjCQL.FindSubmatch(fromByte290)
|
||||
return intoByte758
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAll_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte396 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte707 := mediumObjCQL.ReplaceAll(fromByte396, nil)
|
||||
return intoByte707
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAll_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromByte912 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte718 := mediumObjCQL.ReplaceAll(nil, fromByte912)
|
||||
return intoByte718
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllFunc_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte972 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte633 := mediumObjCQL.ReplaceAllFunc(fromByte972, nil)
|
||||
return intoByte633
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllFunc_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromFuncbytebyte316 := sourceCQL.(func([]byte) []byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte145 := mediumObjCQL.ReplaceAllFunc(nil, fromFuncbytebyte316)
|
||||
return intoByte145
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllLiteral_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromByte817 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte474 := mediumObjCQL.ReplaceAllLiteral(fromByte817, nil)
|
||||
return intoByte474
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllLiteral_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromByte832 := sourceCQL.([]byte)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoByte378 := mediumObjCQL.ReplaceAllLiteral(nil, fromByte832)
|
||||
return intoByte378
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllLiteralString_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString541 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString139 := mediumObjCQL.ReplaceAllLiteralString(fromString541, "")
|
||||
return intoString139
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllLiteralString_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromString814 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString768 := mediumObjCQL.ReplaceAllLiteralString("", fromString814)
|
||||
return intoString768
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllString_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString468 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString736 := mediumObjCQL.ReplaceAllString(fromString468, "")
|
||||
return intoString736
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllString_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromString516 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString246 := mediumObjCQL.ReplaceAllString("", fromString516)
|
||||
return intoString246
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllStringFunc_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString679 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString736 := mediumObjCQL.ReplaceAllStringFunc(fromString679, nil)
|
||||
return intoString736
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpReplaceAllStringFunc_B0I1O0(sourceCQL interface{}) interface{} {
|
||||
fromFuncstringString839 := sourceCQL.(func(string) string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString273 := mediumObjCQL.ReplaceAllStringFunc("", fromFuncstringString839)
|
||||
return intoString273
|
||||
}
|
||||
|
||||
func TaintStepTest_RegexpRegexpSplit_B0I0O0(sourceCQL interface{}) interface{} {
|
||||
fromString982 := sourceCQL.(string)
|
||||
var mediumObjCQL regexp.Regexp
|
||||
intoString458 := mediumObjCQL.Split(fromString982, 0)
|
||||
return intoString458
|
||||
}
|
||||
|
||||
func RunAllTaints_Regexp() {
|
||||
{
|
||||
source := newSource(0)
|
||||
out := TaintStepTest_RegexpQuoteMeta_B0I0O0(source)
|
||||
sink(0, out)
|
||||
}
|
||||
{
|
||||
source := newSource(1)
|
||||
out := TaintStepTest_RegexpRegexpExpand_B0I0O0(source)
|
||||
sink(1, out)
|
||||
}
|
||||
{
|
||||
source := newSource(2)
|
||||
out := TaintStepTest_RegexpRegexpExpand_B0I0O1(source)
|
||||
sink(2, out)
|
||||
}
|
||||
{
|
||||
source := newSource(3)
|
||||
out := TaintStepTest_RegexpRegexpExpand_B0I1O0(source)
|
||||
sink(3, out)
|
||||
}
|
||||
{
|
||||
source := newSource(4)
|
||||
out := TaintStepTest_RegexpRegexpExpand_B0I1O1(source)
|
||||
sink(4, out)
|
||||
}
|
||||
{
|
||||
source := newSource(5)
|
||||
out := TaintStepTest_RegexpRegexpExpandString_B0I0O0(source)
|
||||
sink(5, out)
|
||||
}
|
||||
{
|
||||
source := newSource(6)
|
||||
out := TaintStepTest_RegexpRegexpExpandString_B0I0O1(source)
|
||||
sink(6, out)
|
||||
}
|
||||
{
|
||||
source := newSource(7)
|
||||
out := TaintStepTest_RegexpRegexpExpandString_B0I1O0(source)
|
||||
sink(7, out)
|
||||
}
|
||||
{
|
||||
source := newSource(8)
|
||||
out := TaintStepTest_RegexpRegexpExpandString_B0I1O1(source)
|
||||
sink(8, out)
|
||||
}
|
||||
{
|
||||
source := newSource(9)
|
||||
out := TaintStepTest_RegexpRegexpFind_B0I0O0(source)
|
||||
sink(9, out)
|
||||
}
|
||||
{
|
||||
source := newSource(10)
|
||||
out := TaintStepTest_RegexpRegexpFindAll_B0I0O0(source)
|
||||
sink(10, out)
|
||||
}
|
||||
{
|
||||
source := newSource(11)
|
||||
out := TaintStepTest_RegexpRegexpFindAllString_B0I0O0(source)
|
||||
sink(11, out)
|
||||
}
|
||||
{
|
||||
source := newSource(12)
|
||||
out := TaintStepTest_RegexpRegexpFindAllStringSubmatch_B0I0O0(source)
|
||||
sink(12, out)
|
||||
}
|
||||
{
|
||||
source := newSource(13)
|
||||
out := TaintStepTest_RegexpRegexpFindAllSubmatch_B0I0O0(source)
|
||||
sink(13, out)
|
||||
}
|
||||
{
|
||||
source := newSource(14)
|
||||
out := TaintStepTest_RegexpRegexpFindString_B0I0O0(source)
|
||||
sink(14, out)
|
||||
}
|
||||
{
|
||||
source := newSource(15)
|
||||
out := TaintStepTest_RegexpRegexpFindStringSubmatch_B0I0O0(source)
|
||||
sink(15, out)
|
||||
}
|
||||
{
|
||||
source := newSource(16)
|
||||
out := TaintStepTest_RegexpRegexpFindSubmatch_B0I0O0(source)
|
||||
sink(16, out)
|
||||
}
|
||||
{
|
||||
source := newSource(17)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAll_B0I0O0(source)
|
||||
sink(17, out)
|
||||
}
|
||||
{
|
||||
source := newSource(18)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAll_B0I1O0(source)
|
||||
sink(18, out)
|
||||
}
|
||||
{
|
||||
source := newSource(19)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllFunc_B0I0O0(source)
|
||||
sink(19, out)
|
||||
}
|
||||
{
|
||||
source := newSource(20)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllFunc_B0I1O0(source)
|
||||
sink(20, out)
|
||||
}
|
||||
{
|
||||
source := newSource(21)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllLiteral_B0I0O0(source)
|
||||
sink(21, out)
|
||||
}
|
||||
{
|
||||
source := newSource(22)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllLiteral_B0I1O0(source)
|
||||
sink(22, out)
|
||||
}
|
||||
{
|
||||
source := newSource(23)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllLiteralString_B0I0O0(source)
|
||||
sink(23, out)
|
||||
}
|
||||
{
|
||||
source := newSource(24)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllLiteralString_B0I1O0(source)
|
||||
sink(24, out)
|
||||
}
|
||||
{
|
||||
source := newSource(25)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllString_B0I0O0(source)
|
||||
sink(25, out)
|
||||
}
|
||||
{
|
||||
source := newSource(26)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllString_B0I1O0(source)
|
||||
sink(26, out)
|
||||
}
|
||||
{
|
||||
source := newSource(27)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllStringFunc_B0I0O0(source)
|
||||
sink(27, out)
|
||||
}
|
||||
{
|
||||
source := newSource(28)
|
||||
out := TaintStepTest_RegexpRegexpReplaceAllStringFunc_B0I1O0(source)
|
||||
sink(28, out)
|
||||
}
|
||||
{
|
||||
source := newSource(29)
|
||||
out := TaintStepTest_RegexpRegexpSplit_B0I0O0(source)
|
||||
sink(29, out)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user