残缺查询


19

我知道您一直想着通过网络代理体验生活乐趣的磨难和磨难。老实说,谁没有呢?今天,您的任务是实现这个目标(至少是其中的一部分)。由于大量用户坚持通过查询参数传递敏感信息(用户很愚蠢),因此网站X每天都有大量流量,并且正在寻找PaaS(显然这是指代理即服务)。您的任务是在将请求转发到其原始目的地之前,从请求中删除所有敏感查询参数。

输入项

  • 遵循RFC3986第3节中 URI语法的格式正确的绝对HTTP URL 。
    • 您可以假设没有碎片
    • 简短格式示例,其中方括号中的任何内容表示可选: http[s]://[user:pass@]host.name.com[:port]/[?param1=value1&param2=value2...]
  • 要删除的查询参数列表。

输出量

修改后的HTTP URL,不包含输入列表中定义的参数。

例子

http://example.com/ [foo]
> http://example.com/

http://example.com/?foo=bar []
> http://example.com/?foo=bar

http://example.com/ []
> http://example.com/

http://example.com/?foo=1&bar=2&baz=3 [foo,baz]
> http://example.com/?bar=2

http://example.com/?foo=1&bar=2&baz=3 [foo,bar,baz]
> http://example.com/

http://example.com/?foo&bar=2&baz= [foo,baz]
> http://example.com/?bar=2

http://example.com/?abc=1&def=2&baz=foo [foo,bar]
> http://example.com/?abc=1&def=2&baz=foo

http://example.com/?foobar=baz [foo]
> http://example.com/?foobar=baz

http://foo:foo@foo.com:8080/?foo=1&bar=foo [foo]
> http://foo:foo@foo.com:8080/?bar=foo

计分

这是,因此最短的答案(以字节为单位)获胜。


1
是否可以在单独的行中分别获取URL和查询参数?
seshoumara

1
可以&出现在参数之间以外的任何地方吗?
莱利2016年

也可以例如密码包含一个??还应该保持订单原样吗?
KarlKastor

@Riley编号。如果它&是查询参数的一部分,则应正确将其正确编码为%26
Poke

1
显然,这http://foo:&foo=x@foo.com:8080/?foo=1&bar=foo是RFC允许的。这将打破现有的解决方案。:D(规则为userinfo可以扩展为未保留或pct &=
换码

Answers:


6

GNU sed 98 96 88 80 77 74 69 59 54(48 + 1 for -r)49

:;s,(.+)(=[^&]*[& ]|&)(.*)\1,\3 ,
t;s,[?&]? .*,,

要删除的参数列表以空格分隔。

$ echo 'http://example.com/?foo=1&bar=2&baz=3 foo bar baz' | sed -rf sed.txt
http://example.com/

$ echo 'http://example.com/?foo&bar=2&baz= foo baz' | sed -rf sed.txt
http://example.com/?bar=2

$ echo 'http://example.com/' | sed -rf sed.txt
http://example.com/

在当前的代码编辑中,来自OP问题的多个测试在结果URL中给出了结尾&?字符。
seshoumara

@seshoumara我不确定我是怎么错过的...幸运的是只有1个字节的差异。
莱利

在编辑历史记录中找不到96、77和59字节的代码版本。与编辑6相比,编辑7标题显示的字节数减少了10个字节,但代码并未更改。我正在挑剔,打高尔夫球很棒!
seshoumara

1
@seshoumara我认为它合并了一些编辑内容,因为它们很小(只是删除了几个字符)。
莱利

@seshoumara我猜想它实际上合并了它们,因为我彼此之间在5分钟内进行了多次编辑。
莱利

5

JavaScript(ES6),62 60字节

f=
(s,a,u=new URL(s))=>a.map(e=>u.searchParams.delete(e))&&''+u
;
s.value=document.URL;
<div oninput=o.textContent=f(s.value,a.value.split`\n`)><input id=s><br><textarea id=a></textarea><pre id=o>

编辑:由于@Shaggy,节省了2个字节。


您可以通过.href在末尾删除来节省5个字节。
毛茸茸的

@Shaggy那不会返回字符串...我以为那是不允许的。
尼尔,

这取决于您的输出方式。例如,如果您alert使用它或将其插入到(文本)节点中,则将为您提供href对象的属性。但是,如果将其记录到控制台,它将为您提供完整的对象。看到这个小提琴
毛茸茸的

1
@Shaggy Ah,所以我绝对可以通过对其进行字符串化来节省2个字节,谢谢。
Neil

3

PHP,90字节

<?=trim(preg_replace("#(?<=\?|&)(".join("|",$_GET[r]).")(=.*)?(&|$)#U","",$_GET[u]),"?&");

-11个字节是否?或&末尾允许

先前版本140字节

<?=substr($u=$_GET[u],0,strpos($u,"?")+!!$j=join("&",preg_grep("#^(".join("|",$_GET[r]).")(=|$)#",explode("&",parse_url($u)[query]),1))).$j;

+2个字节:替代项必须加括号,否则^/ (.*|$)将成为第一个/最后一个替代项的一部分。
泰特斯(Titus)

-2个字节:remove .*替换(=.*|$)\b(-5)。
泰特斯(Titus)2016年

您的正则表达式看起来与#^foo|bar(=.*|$)#相同#(^foo)|(bar=.*|bar$))#。但这应该是#(foo|bar)(=.*|$)#
泰特斯(Titus)

@Titus你是对我的错
约尔格Hülsermann

真好!我没有想到断言。这就是我退后的原因array_map(令我惊讶的是结果可能这么短)。
泰特斯(Titus)2016年

2

PHP, 120个 110字节

带有preg_replace和array函数:(受Jörg启发)

<?=preg_replace(array_map(function($s){return"#(\\?|&)$s(=.*)?(&|$)#U";},array_slice($argv,2)),"\1",$argv[1]);

保存到文件,使用 php <scriptname> <uri> <parametername> <parametername> ...

使用parse_str和http_build_query(120字节):

parse_str(end($u=explode('?',$argv[1])),$a);for($i=$argc;$i-->1;)unset($a[$argv[$i]]);echo"$u[0]?".http_build_query($a);

php -r <code> <uri> <parametername> <parametername> ...


parse_strhttp_build_query?我很高兴看到有人使用正确的工具进行工作,即使在打代码时也是如此。由于URL / SQL查询/ regexp / HTML是“字符串”而引起的bug数量众多,它们很容易预防。
Daerdemandt

也许会获得更多灵感。我给你
约尔格Hülsermann

@Lynn除了跟踪我之外,您还有什么事要做吗?
泰特斯(Titus)

2

Java 7,127字节

String a(String a,String[]b){for(String c:b)a=a.replaceAll("(?<=[?&])"+c+"(=[^&]*)?(&|$)","");return a.replaceAll("[?&]$","");}

说明

String sanitize(String url, String[] params) {
    for (String param : params) {
        // please don't modify function parameters in real code
        url=url.replaceAll("(?<=[?&])" // Look for a leading ? or & but don't consume it
            + param                    // Consume the key of the query param (assuming key=value syntax)
            + "(=[^&]*)?"              // Consume the value of the query param if it exists
            + "(&|$)","");             // Consume the trailing & unless we're at the end of the url and replace with nothing
    }
    url = url.replaceAll("[?&]$",""); // If we remove all of the params then we'll have a trailing ? which needs to be removed
                                      // If we remove the last param only then we could have a trailing & which also needs to be removed
                                      // We will only run into one of these scenarios
    return url;
}

伊迪奥


对于我来说,使用您的第4、5、6和9个示例对我来说还是失败的,但是我正在使用Java 8,所以就可以了。尽管尝试了等效的C#,但在相同情况下失败了,所以idunno。
Yodle

1
没关系,搞乱了我的测试方式。
Yodle

2

C#,377 336 330 328字节(173 ALT)

string n(string u,string[]r){var s=u.Split('?');if(s.Length<2)return u;var a=s[1].Contains("&")?s[1].Split('&'):new string[]{s[1]};int B=a.Length,i=0,C=i,c=B;for(;i<B;i++)foreach(var R in r)if(R==a[i].Split('=')[0]){a[i]="";c--;}var t=s[0];t+=c>0?"?":"";for(i=0;i<a.Length;i++)if(a[i]!=""){t+=a[i];C++;if(C!=c)t+="&";}return t;}

完整的程序:

using System;
class a
{
    static void Main()
    {
        string input = Console.ReadLine();
        string url = input.Split(' ')[0];
        string r = input.Split(' ')[1];
        r = r.Replace("[", "").Replace("]","");
        string[] remove = r.Split(',');
        a b = new a();
        Console.WriteLine(b.n(url, remove));
    }
    string n(string u,string[]r)
    {
        var s=u.Split('?');
        if(s.Length<2)return u;
        var a=s[1].Contains("&")?s[1].Split('&'):new string[]{s[1]};
        int B=a.Length,i=0,C=i,c=B;
        for(;i<B;i++)
            foreach(var R in r)
                if(R==a[i].Split('=')[0])
                {
                    a[i]="";
                    c--;
                }
        var t=s[0];
        t+=c>0?"?":"";
        for(i=0;i<a.Length;i++)
            if(a[i]!="")
            {
                t+=a[i];
                C++;
                if (C!=c)t+="&";
            }
        return t;
    }
}

可能不是很有效,但是我认为它可以工作。

或者,使用Java的@Poke方法提供173字节的解决方案。尽管需要正则表达式的导入,所以可能不能更短。

using System.Text.RegularExpressions;string m(string a,string[]b){foreach(var c in b)a=Regex.Replace(a,$"(?<=[?&]){c}(=[^&]*)?(&|$)","");return Regex.Replace(a,"[?&]$","");}

2

Ruby,146140127119116113字节

编辑2:使用$1$2$*和7更改x.split("=")[0]x[/\w+/]
6,以节省6字节编辑3:使用*代替节省6字节,.join从不必要的空格保存2字节
编辑4:通过重新格式化内联(将正则表达式更改为等效项$*[1][/([^?]*)\??(.*)/,1]并放入分配给a
编辑5:通过使用保存3个字节($*[2].scan(r=/\w+/)&[x[r]])[0]代替$*[2].scan(r=/\w+/).include?(x[r])

假设在运行程序时输入程序:

a,b=$*[1][/([^?]*)\??(.*)/,1],$2.split("&").reject{|x|($*[2].scan(r=/\w+/)&[x[r]])[0]}*"&"
puts(b[0] ?a+"?"+b: a)

说明

a,b=$*[1][/([^?]*)\??(.*)/,1],$2.split("&")

这将解析命令行上给出的URL,并将匹配项存储到$1和中$2$*[1][/([^?]*)\??(.*)/,1]还返回第一个匹配项以存储在inside a,而第二个匹配项称为$2 让点指向$ 1,并解析b为数组数组...

.reject { |x|

...拒绝所有...

    ($*[2].scan(r=/\w+/)&[x[r]])[0]

...在第二个参数给定的名称列表中包含在'='之前的字符串...之所以起作用,是因为我们先扫描单词(以获取列表),然后在之前获取单词=,然后查看是否该单词在列表中&。由于&在“未找到”(空集)上返回一个空数组,因此,我们使用下面说明的技巧来获取nil数组中是否没有元素。否则,我们将返回一个字符串,该字符串视为真实字符串,该字符串将拒绝该字符串。

}*"&"

...然后将其余的字符串与'&'连接在一起

此时,b是URL的GET查询字符串。因此,我们只需要打印它。

puts(b[0] ?a+"?"+b: a)

这在红宝石中使用了一个技巧。 b[0]nil如果b为空数组或字符串。因此,如果其值为true,(不是nilfalse),则数组中至少有一个元素,因此我们需要放入a+"?"+b正确的URL。否则,我们就放a,因为没有参数要显示

注意:此答案假设? 无法除了在查询中分隔URL之外,出现在任何地方。(根据我从链接的RFC中读到的内容)

另外,这是我的第一个高尔夫答案:D


2
欢迎来到PPCG!
acrolith

1

点子,46字节

从标准输入中获取URL和从命令行参数中删除的查询参数。

YgqR`\?.+`{s:J_@`^[^=]+`NIyFI@>a^'&[s&'?sJ'&]}

在线尝试!

说明:

 g               Local variable containing list of cmdline args
Y                Yank into global variable y so it's available inside the function
  q              Grab a line of stdin
   R`\?.+`{...}  Do a regex replace of everything from ? on, using a callback function:

s:J_@`^[^=]+`NIyFI@>a^'&[s&'?sJ'&]
                  @>a^'&            All but 1st char of match, split on &
                FI                  Filter on this function:
   _@`^[^=]+`                         Regex match: run of non = from beginning of string
                                      @ returns a list (here, of one item), so...
  J                                   Join to get a scalar
             NIy                      True if match not in y; false if in y
s:                                  Assign the filtered list to s
                        [        ]  Return a list containing:
                         s&'?       ? if s is nonempty, [] otherwise
                             sJ'&   s joined on &
                                    When used as a replacement, a list is first stringified
                                    (which, in the absence of flags, means concatenated)

1

PowerShell v3 +,115个 90字节

param($n,$z)$a,$b=$n-split'\?';($z|%{$b=$b-replace"(^|&)$_(=[^&]*)?(&|$)"});$a+"?"*!!$b+$b

将输入$n作为URL,并将其$z作为字符串的文字数组作为要删除的参数。-split将输入URL放在上?,将前一半存储到,将后一半存储$a$b

接下来,$b通过循环遍历$z-replace对每个被禁止的查询词执行正则表达式以将其删除,从而重新制定公式。然后,输出$a(未修改),加上一个/取决于是否$b存在,加上一个?取决于是否存在$x存在,再加上$ x。


1

Pyth-27个字节

肯尼(Kenny)谈到转换和反转的内建函数时是对的,尽管如此,这很难做到。

.sjK\?mj\&f!}hcT\=Qcd\&czKK

测试套件


1

视网膜44 48字节

划掉的44仍然是44。感谢Martin。

[?&](?>([^ =&]+))[^ &]*(?=.* \1( |$))| .*

/&
/?

接受类似的输入uri param1 param2在线尝试!

说明

第一次替换将从查询字符串中删除适当的参数。[?&](?>([^ =&+))[^ &]*匹配一个?&,一个完整的参数名称=以及一个(可选)一个值,并将参数名称存储在捕获组1中。(?=.* \1( |$))。如果参数符合这些条件,则将其删除(替换为空替换)。

替换是不重叠的(由于先行),并且从左到右进行替换。到达网址末尾时, .*分支与要删除的参数列表匹配,也将其删除。

?如果删除了第一个参数,则第二个替换操作只是确保新的查询字符串以“。”开头。


我认为,如果参数的前缀出现在列表的末尾(例如,请尝试retina.tryitonline.net/…),这也会删除参数。解决此问题的一种方法是将组1包装在中(?>...)
马丁·恩德

@MartinEnder TIL有关非回溯子表达式。谢谢!
DLosc

0

Java 7,203字节

String f(String u,List p)throws Exception{String[]g=u.split("\\?",2);String s="";if(g.length>1)for(String q:g[1].split("&")){if(p.indexOf(q.split("=")[0])<0){s+=s.isEmpty()?"?":"&";s+=q;}}return g[0]+s;}

取消高尔夫:

  String f(String u, List p) throws Exception {
    String[] g = u.split("\\?", 2);
    String s = "";
    if (g.length > 1) for (String q : g[1].split("&")) {
      if (p.indexOf(q.split("=")[0]) < 0) {
        s += s.isEmpty() ? "?" : "&";
        s += q;
      }
    }
    return g[0] + s;
  }

此功能通过所有测试。


0

Python,第75个 81 112字节:

def Z(A,S):import re;F=A.rindex('/');print A[:F]+re.sub('|'.join(i+'(=\d?|&)&?'for i in S),'',A[F:]).strip('&?')

命名函数。以以下格式输入

D(<String>,<Array>)

并输出一个字符串。

使用所有测试用例!


0

PHP,不竞争

哎呀,PHP是为此而设计的;为什么不使用实际的URL?

<?foreach($_GET[x]as$w)unset($_GET[$w]);
echo http,s[$_SERVER[SERVER_PORT]-443],"://",
$u=$_SERVER[PHP_AUTH_USER],($p=$_SERVER[PHP_AUTH_PW])?":$p":"","@"[!$u&!$p],
"$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]?",http_build_query($_GET);

保存到文件,使用所需的查询字符串加进行调用&x[]=x&x[]=<exclude1>&x[]=<exclude2>&...

用户名和密码可能会失败(取决于您的浏览器是否将其删除)。如果密码为,
失败0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.